
/***************************************************************************
 *   Copyright (C) 1997 to 2004 by Jonathan Duddington                     *
 *   email: jonsd@users.sourceforge.net                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write see:                           *
 *               <http://www.gnu.org/licenses/>.                           *
 ***************************************************************************/

/* Changed to use TEXTR * */
/* Changes Font Colours for highlight */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#include "alarm.h"
#include "font.h"
#include "os.h"
#include "wimp.h"
#include "bbc.h"
#include "event.h"
#include "werr.h"
#include "dbox.h"
#include "menu.h"
#include "xfersend.h"
#include "xferrecv.h"
#include "flex.h"
#include "template.h"
#include "msgs.h"
#include "win.h"
#include "wimpt.h"
#include "akbd.h"

#include "narc.h"
#include "hdrs.h"

#ifdef MemCheck
#include "MemCheck:Flex.h"
#endif


#define OS_v40   0xa8
#define OS_v50  0xaa     /* OS version for Iyonix */

#define TEXT_Y_OFFSET   8


/* 13.688 pt  at 78% ascpect ratio ??? */





extern OPTIONS options;
extern int timezone_offset;
extern FOLDREC src_fr;
extern FOLDREC cty_fr;
extern int os_version;


extern int dbox_menu_field;

extern wimp_menustr *wmenu_binned;
extern wimp_menustr *wmenu_boxes_names;
extern menu menu_boxes_names;
extern int y_screen_dim;   /* screen height for current mode */
int y_screen_min = 0;   /* min. y value if restricted by icon bar */
extern int scroll_bar_width;
extern int scroll_bar_height;

extern int scroll_as_you_speak;

extern BOX box_table[];
extern int  boxes_count[64];
extern int  boxes_unread_count[64];

extern int article_filetypes[];
extern char chars_lc[256];

extern int fontn[N_FONTS];
extern char *fontname[N_FONTS];
extern int font_spacing[N_FONTS];
extern int font_offset[N_FONTS];

TEXTR *text_view=NULL;
extern TEXTR *text_view_iconised;
extern TEXTR *textr_cats;
extern TEXTR *textr_spellcheck;

extern int scale_x;
extern int scale_y;
extern int n_screen_colrs;



TEXTR *text_data_record[N_TEXT_DATA];
int text_backg_colr=0;
char menu_get_number[8];

int text_font;			/* font handle */
int text_line_height =   40;      /* line spacing in os units */
int text_font_offset;

int print_font;
int print_line_height;

int text_monospaced = 0;          /* num of characters per line if monospaced font */
int text_monospaced_reply = 0;    /* num of characters per line in reply */
int text_monospaced_width;        /* character width in millipoints */
int text_max_char_width;
int page_width =  1000;
int page_height = 1000;  /* ??? */
int margin_l =      10;
int margin_r =       0;
int margin_r_reply = 36;

int drag_last_line = 0;
int drag_last_x = 0;
int drag_window_type = 0;
void *drag_handle = NULL;
wimp_w drag_window = NULL;

static TEXTR *current_t;
static TEXTR *t_region;          /* the text window which may contain the selected region */
static int text_selected_box_copy = 0;   /* move or copy into box */
static int drag_point = 0;
static int escape_flag = 0;
static int attach_window_height;
extern char menu_score_value[8];



MNEM_TAB2 header_lines[N_MARKED_HEADER_LINES] = {
	NULL, 0, 0
};

/* for >16 colours, entry 0 is for internet headers & sigs */

int text_colrs_256[] = {
	0x000000,    /* black */
	0xcc0022,    /* url: red */
	0x228800,    /* headers: green */
	0xaa6622,    /* sig: green */
	0x0044aa,    /* qu1: blue */
	0x007755,    /* qu2: green-blue */
	0x11aa00,    /* qu3: green */
	0x770077,    /* qu4: purple */
	0x770077,    /* headers2: purple */
	0x0044aa,    /* spare: blue */
};

int text_colrs_256_inv[] = {
	0xffffff,    /* white */
	0xff8888,    /* url: red */
	0xeedd22,    /* headers: green */
	0xeecc22,    /* sig: green */
	0x66bbff,    /* qu1: blue */
	0x55ffdd,    /* qu2: green-blue */
	0x44ff88,    /* qu3: green */
	0x99ff44,    /* qu4: purple */
	0x99ff44,    /* headers2: purple */
	0x66bbff,    /* spare: blue */
};


int text_colrs_256_green[] = {
	0xdddddd,    /* white */
	0xffcc77,    /* url: red */
	0x22ddee,    /* headers: green */
	0xbbbbbb,    /* sig: green */
	0x55ccff,    /* qu1: blue */
	0x00bbcc,    /* qu2: green-blue */
	0x22aacc,    /* qu3: green */
	0xcccc88,    /* qu4: purple */
	0x11ee00,    /* headers2: purple */
	0xff00cc,    /* spare: blue */
};  /* for dark green background */



static int text_colrs_16[] = {
	0x000000,    /* black */
	0xcc0022,    /* url: red */
	0x228800,    /* headers: green */
	0x228800,    /* sig: green */
	0x0044aa,    /* qu1: blue */
	0x558800,    /* qu2: dark green */
	0x00cc00,    /* qu3: green */
	0x00cc00,    /* qu4: green */
	0x0044aa,    /* headers2: blue */
	0x0044aa,    /* spare: blue */
};

int text_colrs_16_inv[] = {
	0xffffff,    /* white */
	0xdd0000,    /* url: red */
	0xeeee00,    /* headers: green */
	0xeeee00,    /* sig: green */
	0x00bbff,    /* qu1: blue */
	0x00cc00,    /* qu2: green-blue */
	0x558800,    /* qu3: green */
	0x558800,    /* qu4: purple */
	0x558800,    /* headers2: purple */
	0x00bbff,    /* spare: blue */
};


#define N_TEXT_COLRS 9

static char colr_text[N_TEXT_COLRS][8];

static char white_on_black[8] = {19,0x55,0x55,0x55,0xff,0xff,0xff,8};
static char black_on_white[8] = {19,0xff,0xff,0xff,0,0,0,8};
static char *colr_inverse = white_on_black;
static unsigned int stripe_foreg = 0xffffff00;
static unsigned int stripe_backg = 0;
static unsigned int colour_backg = 0xffffff00;  /* window background */
static unsigned int colour_backg_fill;

#define COLR_TEXT   0
#define COLR_URL    1
#define COLR_HEADER 2
#define COLR_SIG    3
#define COLR_QUOTE  4
#define COLR_HEADER2 8

/* remember to change the switch[] in text_menu_proc, and the menu_submenu() in init_text */
static menu menu_viewer;
static menu menu_edit2;
static menu menu_message;
static menu menu_url;
extern menu menu_write;    /* write mail, write news */
extern menu menu_pgp;
static char *menustr_view = "Display,Message,Status,Edit,Goto,Send,Cancel news,Misc";
static char *menustr_display = "Format ^F,Swap font ^F,HTML to text ^G,Header ^H,Rot13 ^O,Decrypt/Verify ^Y,Encrypt ^Y";
static char *menustr_edit2 = "Allow Edit ^E,Quote Text ^Q,Unquote";
static char *menustr_message = "Close ^F2,>Save as F3,Save back F3,Print PRINT,Move to box F6,Copy to box F6,Archive ^R,Fetch body ^B,Delete ^K,Remove attachments";
static char *menustr_url = "New email,Add to CC,Add to BCC,Add to address book,Add to write window,Fetch URL,Message-ID,Google archive,Save as";

static char *menustr_status = "Unread ^U,Unlock/Read ^E,Lock  ^L,Mark  ^M,Add replied,Remove replied,Score";
static char *menustr_goto = "References,Previous ^P,Next ^N,Skip thread ^T,Delete thread ^T";
static char *menustr_send = "Mail reply ^WR,News followup ^WF,Forward ^WD,Bounce ^WB,Blank mail ^WM,Blank news ^WN,Fake undeliverable ^WU";
static char *menustr_misc = "Find F4,Addr book addF7,Speak F8,Spelling F9,Allow edit ^E,Killfile add";

static char *menustr_killfile = "Title start,Title,Author,Domain,News thread,Sub thread";
static char *menustr_cancel = "Cancel,Supersede";
int text_menu_flag_url = 0;
int text_menu_index = 0;


static char *clipboard = NULL;
static int  clipboard_size = 0;

static int  search_length_str = 0;
static int  search_case_sensitive = 0;


wimp_wind *text_window_template=NULL;

#define N_REORDER 10
static int  n_reorder;
static int  reorder_pos[N_REORDER];
static int  reorder_len[N_REORDER];

#define N_BOUNDARIES 3
#define N_BOUNDARY_LEN 80
static int  n_boundaries;
static int  boundary_string_len[N_BOUNDARIES];
static char boundary_string[N_BOUNDARIES][N_BOUNDARY_LEN];





/*********************************************************************/

static unsigned short latinA[256] = {
	0x40C0, 0x40E0, 0x20C3, 0x20E3, 0x20A1, 0x20B1, 0x20C6, 0x20E6,
	0x30C6, 0x30E6, 0x30C5, 0x30E5, 0x20C8, 0x20E8, 0x20CF, 0x20EF,

	0x20D0, 0x20F0, 0x40AA, 0x40BA, 0x0000, 0x0000, 0x40CC, 0x40EC,
	0x20CA, 0x20EA, 0x20CC, 0x20EC, 0x30D8, 0x30F8, 0x30AB, 0x30BB,

	0x30D5, 0x30F5, 0x40AB, 0x40BB, 0x30A6, 0x30B6, 0x30A1, 0x30B1,
	0x40A5, 0x40B5, 0x40CF, 0x40EF, 0x0000, 0x0000, 0x40C7, 0x40E7,

	0x30A9, 0x30B9, 0x0000, 0x0000, 0x30AC, 0x30BC, 0x40D3, 0x40F3,
	0x40A2, 0x20C5, 0x20E5, 0x40A6, 0x40B6, 0x20A5, 0x20B5, 0x0000,

	0x0000, 0x20A3, 0x20B3, 0x20D1, 0x20F1, 0x40D1, 0x40F1, 0x20D2,
	0x20F2, 0x0000, 0x40BD, 0x40BF, 0x40D2, 0x40F2, 0x0000, 0x0000,

	0x20D5, 0x20F5, 0x009A, 0x009B, 0x20C0, 0x20E0, 0x40A3, 0x40B3,
	0x20D8, 0x20F8, 0x20A6, 0x20B6, 0x40DE, 0x40FE, 0x20AA, 0x20BA,

	0x20A9, 0x20B9, 0x20DE, 0x20FE, 0x20AB, 0x20BB, 0x40AC, 0x40BC,
	0x40DD, 0x40FD, 0x40DE, 0x40FE, 0x30DD, 0x30FD, 0x20D9, 0x20F9,

	0x20DB, 0x20FB, 0x40D9, 0x40F9, 0x1081, 0x1082, 0x1085, 0x1086,
	0x0000, 0x20AC, 0x20BC, 0x20AF, 0x20BF, 0x20AE, 0x20BE, 0x0000
};


static struct {
	unsigned short unicode;
	char  charset;
	char  value;
} unicode_tab[] = {

	/*  Latin Extended-B */

	0x02BC, 0, 0x91,
	0x02C7, 2, 0xB7,
	0x02D8, 2, 0xA2,
	0x02D9, 2, 0xFF,
	0x02DB, 2, 0xB2,
	0x02DD, 2, 0xBD,


	/* General Punctuation */

	0x2010, 0, 0xAD,
	0x2013, 0, 0x97,
	0x2014, 0, 0x98,
	0x2018, 0, 0x90,
	0x2019, 0, 0x91,
	0x201C, 0, 0x94,
	0x201D, 0, 0x95,
	0x201E, 0, 0x96,
	0x2020, 0, 0x9C,
	0x2021, 0, 0x9D,
	0x2022, 0, 0x8F,
	0x2026, 0, 0x8C,
	0x2030, 0, 0x8E,
	0x2039, 0, 0x92,
	0x203A, 0, 0x93,

	/* Currency */
	0x20ac, 0, 0xa4,   /* euro */

	/* Letterlike Symbols */

	0x2122, 0, 0x8D,

	/* Mathematical Operators */

	0x2212, 0, 0x99,

	/* Alphabetic Presentation Forms */

	0xFB01, 0, 0x9E,
	0xFB02, 0, 0x9F,

	0,0,0,
};

#define N_CHARSETS  8


int utf8_decode(TEXTR *t, int start, int end)
/*******************************************/
/* Convert a string of bytes to a unicode character */
{
	char *p;
	char *p_end;
	char *p_out;
	int  z;
	int  y;
	int  z2;
	unsigned int  acc;
	unsigned int  acc1;
	int  charset;
	int  n;
	int  i;
	int  reduction;

	int  charset_count[N_CHARSETS];

	static int errchar='#';

	static unsigned char b[7] = {0,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe};
	static unsigned char err[6] = {0,0xc2,0xa0,0x90,0x88,0x84};

	memset(charset_count,0,sizeof(charset_count));

	p = &t->text_base[start];
	p_end = &t->text_base[end];
	p_out = p;

	while(p < p_end)
	{

		/* translate UTF-8 sequence into unicode character */
		z = *p++;

		if(z < 0x80)
			acc = z;
		else
		{
			for(n=0; n<6; n++)
			{
				if(z < b[n+1])
					break;
			}

			if((n==0) || (n > 5))
			{
				acc = errchar;
			}
			else
			{
				if(n==1)
					z2 = p[-1];
				else
					z2 = *p;  /* 2nd byte in the sequence */

				acc = acc1 = z - b[n];

				for(i=0; i<n; i++)
				{
					y = *p++;
					if((y >= 0xc0) || (y < 0x80))
					{
						acc = errchar;
						break;
					}
					acc = (acc << 6) + (y-0x80);
				}

				if(((n==1) || (acc1 == 0)) && (z2 < err[n]))
					acc = errchar;   /* overlong sequence */
			}
		}

		/* lookup unicode character */

		if(acc > 0x7f)
		{
			/* find equivalent ascii char and charset */
			if(acc < 0x100)
			{
				charset = 1;
			}
			else if(acc < 0x17f)
			{
				/* Latin extended A */
				acc = latinA[acc-0x100];
				charset = acc >> 12;
				acc = acc & 0xff;
			}
			else if((acc >= 0x380) && (acc < 0x3d0))
			{
				/* Greek */
				charset = 7;
				acc = acc - 0x2d0;
			}
			else if((acc >= 0x400) && (acc < 0x460))
			{
				/* Cyrillic */
				charset = 5;
				acc = acc - 0x360;
			}
			else
			{
				for(i=0;; i++)
				{
					if((z = unicode_tab[i].unicode) == acc)
					{
						charset = unicode_tab[i].charset;
						acc = unicode_tab[i].value;
						break;
					}
					else if(z == 0)
					{
						acc = 0;
						break;
					}
				}
			}

			if(acc == 0)
			{
				acc = errchar;
			}
			else
			{
				charset_count[charset]++;
			}
		}
		*p_out++ = acc;
	}

	reduction = &t->text_base[end] - p_out;
	if(reduction > 0)
	{
		move_up(t,end,reduction);
	}

	/* find the most-used character set */
	n = 0;
	charset = 0;
	for(i=1; i<N_CHARSETS; i++)
	{
		if(charset_count[i] > n)
		{
			n = charset_count[i];
			charset = i;
		}
	}
	t->charset = charset;
	return(charset);
}   /* end utf8_decode */

/*********************************************************************/





void speak_titles(TEXTR *t)
/*************************/
/* Speak the title and author from a text_view */
{
	int  i;
	int  c;
	char *from_to;
	char name[128];
	char buf[512];

	if(t->text_type <= X_VIEW2)
	{
		/* omit <email addr> from name */
		i = 0;
		while((c = t->cardex->author[i]) != 0)
		{
			if((c == '<') && (i > 0))
				break;

			name[i++] = c;
		}
		name[i] = 0;

		if(t->cardex->status_other & STATUS_BIT_OG)
			from_to = "To";
		else
			from_to = "From";

		sprintf(buf,". Title. %s. %s. %s. .",t->cardex->title,from_to,name);
		announce_message(buf,0);
	}
}   /* speak_titles */




void dbox_text_corner(TEXTR *t, dbox d)
/*************************************/
/* Open the dbox at the top right corner of the text window */
{
	int  x_size;
	int  y_size;
	wimp_wstate wstate_t;
	wimp_wstate wstate;

	wimp_get_wind_state(t->w_card,&wstate_t);

	wimp_get_wind_state(dbox_syshandle(d),&wstate);
	x_size = wstate.o.box.x1 - wstate.o.box.x0;
	y_size = wstate.o.box.y1 - wstate.o.box.y0;

	wstate.o.box.x1 = wstate_t.o.box.x1-8;
	wstate.o.box.x0 = wstate.o.box.x1 - x_size;
	wstate.o.box.y1 = wstate_t.o.box.y1-4;
	wstate.o.box.y0 = wstate.o.box.y1 - y_size;
	wimp_open_wind(&wstate.o);
}   /* dbox_text_corner */




void set_text_extent(TEXTR *t)
/*************************/
{
	int  i;
	int min_extent;

	wimp_redrawstr w_workarea;

	if(t->text_type <= X_VIEW2)
		min_extent = 400;
	else
		min_extent = 700;


	i = t->extent_y;
	if(t->extent_y < min_extent)
		i = min_extent;

	if(t->ext_edit)
		i = 148;

	t->workarea.box.x0 = 0;
	t->workarea.box.y0 = -i;
	t->workarea.box.x1 = page_width;
	t->workarea.box.y1 = 0;
	t->workarea.w = t->w_text;
	wimp_set_extent(&t->workarea);

	memcpy(&w_workarea,&t->workarea,sizeof(w_workarea));
	w_workarea.box.y0 = -(i  + t->button_bar_height);
	w_workarea.box.x1 = page_width+scroll_bar_width;
	w_workarea.w = t->w_card;
	wimp_set_extent(&w_workarea);

}   /* end of set_text_extent */



void set_attach_window_height(TEXTR *t)
/*************************************/
{
	if(t->attachments <= 6)
		attach_window_height = 132;
	else if(t->attachments <= 12)
		attach_window_height = 252;
	else
		attach_window_height = 272;
}   /* end of set_attach_window_height */




static int update_unread_count(CARD_EXPANDED *cardex)
/***************************************************/
/* Returns: 1=box read/unread state has changed */
{
	int  stat;
	int  change = 0;
	int  i;

	stat = cardex->card_ptr->status & STATUS_MASK;

	if((cardex->status <= STATUS_UNREAD) && (stat > STATUS_UNREAD))
		change = 1;
	if((cardex->status > STATUS_UNREAD) && (stat <= STATUS_UNREAD))
		change = -1;

	if(change != 0)
	{
		i = boxes_unread_inc(cardex->docbox,change);
		redraw_boxes_list(cardex->docbox);
		return(i);
	}
	return(0);
}   /* end of update_unread_count */



void text_show_status(TEXTR *t)
/*****************************/
/* Update the article status icon in the article viewer */
{
	int  status;
	int  replied;
	int  box;
	char buf[12];

	status = t->cptr->status & STATUS_MASK2;
	box = t->cptr->date_box >> 26;
	replied = status & STATUS_BIT_REPLIED;
	if(status != STATUS_FETCHING)
	{
		status &= STATUS_MASK;
		if(replied)
			status |= 0x10;
	}

	if((t->text_type <= X_VIEW2) && (t->cptr != NULL))
	{
		sprintf(buf,"stat_%.2x\r",status);
		if(t->status_sprite != NULL)
			memcpy(t->status_sprite,buf,8);
		wimp_set_icon_state(t->w_card,CD_STATUS_SPRITE,0,0);
		dbox_setfield(t->dbox_card, CD_BOXIN, get_box_name(box));
	}
}   /* end of text_show_status */



void text_show_status2(CARD *cptr)
/********************************/
/* Look for any text records which show this card and update their status icon */
{
	int  i;
	TEXTR *t;

	for(i=0; i<N_TEXT_DATA; i++)
	{
		if(((t = text_data_record[i]) != NULL) && (t->cptr == cptr))
			text_show_status(t);
	}
}   /* end of text_show_status2 */



/************************************************************************/
/************************************************************************/



static int confirm_delete_article(TEXTR *t, CARD_EXPANDED *cardex)
/****************************************************************/
{
	if(card_check_changed(t) != 0)
		return(0);

	if(options.confirm_delete == 0)
	{
		if(query("Delete article ?","DELETE") == 0)
			return(0);
	}
	else

		if((options.confirm_delete == 1) &&
				((cardex->status == STATUS_MARKED) || (cardex->status == STATUS_LOCKED)))
		{
			if(query("Delete locked article ?","DELETE") == 0)
				return(0);
		}
	return(1);
}   /* end of confirm_delete_article */




int close_article_window(TEXTR *t)
/****************************/
{
	wimp_close_wind(t->w_text);
	/*   wimp_close_wind(t->w_card); */

	if(t->dbox_card != 0)
		dbox_hide(t->dbox_card);

	wimp_close_wind(t->w_attach);

	if(t == textr_spellcheck)
	{
		close_spell_window(t);
	}

	t->allow_edit = 0;

	if(t->text_type == X_VIEW2)
	{
		text_data_free(t);
	}
	return(0);
}   /* end of close_article_window */




static void open_article_window(TEXTR *t, wimp_openstr *o, int initial)
/*********************************************************************/
/* Open the text window, as specified.  Also open the
   button bar */
{
	int max_y;
	int  dy;
	int  i;
	wimp_openstr b;
	wimp_wstate wstatet;
	wimp_wstate wstate;


	if(t == NULL) return;

	set_attach_window_height(t);

	/* make sure there's room for button bar above the text window */
	max_y = y_screen_dim - (t->button_bar_height + scroll_bar_height*2 - 2);

	dy = (o->box.y1 - o->box.y0) - max_y;
	if(dy > 0)
	{
		/* restrict size of window */
		o->box.y0 += dy;
	}

	max_y = y_screen_dim - (t->button_bar_height + scroll_bar_height - 2);
	if(o->box.y1 > max_y)
	{
		/* too near top of screen, allow space for button bar  */
		dy = o->box.y1 - max_y;
		o->box.y1 -= dy;
		o->box.y0 -= dy;
	}

	if(o->box.y0 < y_screen_min)
	{
		/*   o->box.y0 = y_screen_min; */
	}


	/* ensure we don't start partway down a line */
	if(((i = o->y - t->prev_scroll_posn) > 35) || (i < - 35))
	{
		/* scroll by more than one scroll unit */
		if(i > 0)
			o->y -= text_line_height;

		i = (o->y + 4) % text_line_height;
		o->y -= i;
	}
	t->prev_scroll_posn = o->y;

	if(t->attachments)
	{
		/* leave room for attachments window at the bottom */
		if(o->box.y0 < (attach_window_height + scroll_bar_height))
			o->box.y0 = attach_window_height + scroll_bar_height;
	}
	else
	{
		wimp_close_wind(t->w_attach);
	}

	wimp_open_wind(o);     /* open text window */
	wimp_get_wind_state(t->w_text,&wstatet);

	b.box.x0 = wstatet.o.box.x0;
	b.box.x1 = wstatet.o.box.x1 + scroll_bar_width;
	b.box.y0 = wstatet.o.box.y0;
	b.box.y1 = wstatet.o.box.y1 + t->button_bar_height;
	b.x = 0;
	b.y = 0;
	b.w = t->w_card;
	b.behind = t->w_text;
	wimp_open_wind(&b);    /* open button bar and title bar */

	if(t->attachments)
	{
		b.w = t->w_attach;
		b.box.x0 = wstatet.o.box.x0;
		b.box.x1 = wstatet.o.box.x1;
		b.box.y1 = wstatet.o.box.y0 - scroll_bar_height;
		b.box.y0 = b.box.y1 - attach_window_height;
		b.behind = t->w_text;
		wimp_open_wind(&b);

		wimp_get_wind_state(t->w_attach,&wstate);
		if(memcpy(&b.box,&wstate.o.box,sizeof(b.box)) != 0)
		{
			b.box.x0 = wstatet.o.box.x0;
			b.box.x1 = wstatet.o.box.x1;
			b.box.y1 = wstatet.o.box.y0 - scroll_bar_height;
			b.box.y0 = b.box.y1 - attach_window_height;
			wimp_open_wind(&b);    /* open twice to avoid it being forced fully on screen */
		}
	}
}   /* end of open_article_window */








void open_card_window(wimp_openstr *o, TEXTR *t)
/**********************************************/
/* Open the button bar window, as specified.  Also open the
    text window */
{
	int behind;
	int  top_window;
	wimp_w this_w;

	wimp_wstate wstate;
	wimp_wstate wstate_t;

	if((t == text_view_iconised) && (o->behind != -3))
		text_view_iconised = NULL;

	this_w = o->w;
	wimp_get_wind_state(t->w_text,&wstate_t);

	if(o->box.y0 < y_screen_min)
	{
		/*   o->box.y0 = y_screen_min; */
	}


	if(t->attachments)
	{
		set_attach_window_height(t);

		if(o->box.y0 < (attach_window_height+scroll_bar_height))
		{
			o->box.y0 = attach_window_height + scroll_bar_height;
		}
	}

	behind = top_window = o->behind;

	if(behind <= -2)
	{
		if(behind == -3)
			wimp_open_wind(o);

		wstate_t.o.behind = behind;
		wimp_open_wind(&wstate_t.o);

		if(t->attachments)
		{
			wimp_get_wind_state(t->w_attach,&wstate_t);
			wstate_t.o.behind = behind;
			wimp_open_wind(&wstate_t.o);
		}

		/* put to back */
		if(behind == -2)
			wimp_open_wind(o);

		return;
	}


	if((behind == -1) && (wstate_t.o.behind == -1))
		o->behind = t->w_text;

	behind = o->behind;
	wimp_open_wind(o);   /* open button bar & title bar */

	wimp_get_wind_state(this_w,&wstate);

	wstate_t.o.box.x0 = wstate.o.box.x0;
	wstate_t.o.box.x1 = wstate.o.box.x1 - scroll_bar_width;
	wstate_t.o.box.y1 = wstate.o.box.y1 - t->button_bar_height;
	wstate_t.o.box.y0 = wstate.o.box.y0;

	if(behind == -1)
		wstate_t.o.behind = -1;
	else
		wstate_t.o.behind = top_window;
	wimp_open_wind(&wstate_t.o);    /* open text window */

	wstate.o.behind = t->w_text;
	wimp_open_wind(&wstate.o);  /* reopen card window to ensure it's behind the text window */


	if(t->attachments)
	{
		wstate_t.o.box.y1 = wstate.o.box.y0 - scroll_bar_height;
		wstate_t.o.box.y0 = wstate_t.o.box.y1 - attach_window_height;
		wstate_t.o.w = t->w_attach;
		wstate_t.o.behind = t->w_text;
		wstate_t.o.y = 0;

		wimp_open_wind(&wstate_t.o);    /* open attachments window */
	}
	else
	{
		wimp_close_wind(t->w_attach);
	}

}   /* end of open_card_window */






char *de_tab_text(char *p,int n_chars)
/************************************/
/* Copy text into buffer, replacing tabs by spaces */
{
	static char detab_buf[256];   /* not on the stack */

	int  c;
	int  ix;

	if((n_chars < 0) || (n_chars >= (sizeof(detab_buf)-1)))
	{
		n_chars = sizeof(detab_buf)-2;
	}

	ix = 0;
	while(n_chars-- > 0)
	{
		c = *p++;

		if(c < ' ')
		{
			if(c == '\n') break;

			if(c == '\t')
			{
				c = ' ';
#ifdef deleted
				/* we wouldn't have a correspondence between cursor and character position */
				while((ix & 7) != 7)
				{
					/* spaces up to 'multiple of 8' tab position */
					detab_buf[ix++] = c;
				}
#endif
			}
			else
				c = '#';
		}

		detab_buf[ix++] = c;
	}
	detab_buf[ix] = '\n';

	return(detab_buf);
}   /* end of de_tab_text */




int index_to_linestart(TEXTR *t, int index, int *line_out)
/********************************************************/
/* Find the line number for an index into the text */
{
	int  line;
	int  start_ix;
	int  prev_ix=0;
	int  i;

	start_ix = t->text_start;
	for(line=0; line<t->n_lines; line++)
	{
		i = start_ix + t->line_tab[line];
		if(start_ix > index)
			break;
		prev_ix = start_ix;
		start_ix = i;
	}
	if(line_out != NULL)
		*line_out = line-1;
	return(prev_ix);
}   /* end of index_to_linestart */




int warea_to_line(TEXTR *t, int wa_y, int *y_out, int *index_out, int height)
/***************************************************************************/
/* Find the line on the screen for a given workarea coordinate */
{
	int  y=0;
	int  line=0;
	int  index;

	wa_y += TEXT_Y_OFFSET;

	index = t->text_start;
	y = -height;
	while(line < (t->n_lines-1))
	{
		if((y - height/4) < wa_y)
			break;
		index += t->line_tab[line];
		line++;
		y -= height;
	}
	if(y_out != NULL) *y_out = y - TEXT_Y_OFFSET;
	if(index_out != NULL) *index_out = index;
	return(line);
}   /* end of warea_to_line */




int line_to_warea(int this_line,int height)
/*****************************************/
{
	int  y;

	y = ((this_line + 1)) * height + TEXT_Y_OFFSET;
	return(-y);
}   /* end of line_to_warea */





void cursor_to_warea(TEXTR *t, int *wx, int *wy)
/**********************************************/
/* Finds the work area coords for the current cursor position,
   sets cursor_line and cursor_line_start */
{
	int  line;
	int  y=0;
	int  count;
	int  line_start;
	char *p;
	int  offset;
	int  padding;
	int  max_x = 0x7fff;
	os_regset regs;
	static int coord_block[8] = {0,0, 0,0, 0,0, 0,0};

	count = t->text_start;
	for(line=0; line<t->n_lines; line++)
	{
		count += t->line_tab[line];
		y -= text_line_height;

		if(count > t->cursor_index)
			break;

	}
	*wy = y - TEXT_Y_OFFSET;
	line_start = count - t->line_tab[line];

	if(line_start < 0)
		line_start = 0;
	t->cursor_line_start = line_start;
	t->cursor_line = line;

	/* find x coordinate */
	p = &t->text_base[line_start];
	p = de_tab_text(p,t->line_tab[line]);

	/*   get_line_padding(p,line,justification,&offset,&padding); */
	offset = 0;
	padding = 0;

	if(text_font <= 0)
	{
		count = t->cursor_index - line_start;
		count = text_monospaced_width * count;
		*wx = margin_l + count/scale_x;
	}
	else
	{
		regs.r[0] = text_font;
		regs.r[1] = (int)p;
		regs.r[2] = 0x20180;  /* bits 17,8, 7 */
		regs.r[3] = max_x * scale_x;
		regs.r[4] = text_line_height * scale_y;
		regs.r[7] = t->cursor_index - line_start;

		if(padding > 0)
		{
			coord_block[0] = padding;
			regs.r[2] = 0x201a0;   /* bits 17,8,7,5 */
			regs.r[5] = (int)coord_block;
		}
		os_swix(0x400a1+os_X,&regs);   /* Font_ScanString */
		*wx = ((regs.r[3]+offset) / scale_x) + margin_l;
	}
}   /* end of cursor_to_warea */




int set_caret_pos2(TEXTR *t)
/**************************/
/* Set the caret at the current cursor position */
{
	int  x,y;
	wimp_caretstr caretstr;

	/* ensure the cursor is within the text */
	if(t->cursor_index >= t->text_body_end)
		t->cursor_index = t->text_body_end - 1;
	if(t->cursor_index < 0)
		t->cursor_index = 0;

	cursor_to_warea(t,&x,&y);


	caretstr.w = t->w_text;
	caretstr.x = x;
	caretstr.y = y;
	caretstr.i = -1;
	caretstr.height = text_line_height;
	caretstr.index = 0;

	wimp_set_caret_pos(&caretstr);
	return(y);
}   /* end of set_caret_pos2 */




void set_caret_pos(TEXTR *t)
/**************************/
{
	int  y;

	y = set_caret_pos2(t);
	t->last_cursor_index = t->cursor_index;

	/* scroll to ensure cursor is on the screen */
	scroll_to_y(t->w_text,y);
	t->prev_scroll_posn = 0xffff;
}   /* end of set_caret_pos */




static int scan_line(TEXTR *t, int displ, int *height, int *width)
/****************************************************************/
/* How many charcaters from 'p' fit into the page width (adjusted by
   margins).  Also return height and width of string */
{

	/* NOTE: height, width not used */

	int  n_chars;
	int  i;
	char *p;
	char *p_end;
	char *p_old;
	char *text_end;
	int  max_n_chars;
	int  monospaced;
	int  blanks;
	os_regset regs;
	static int scan_block[9] = {0,0, 0,0, ' ', 0,0, 0,0};

	p = t->text_base + displ;
	text_end = &t->text_base[t->text_body_end];
	max_n_chars = text_end - p;

	p_old = p;
	p = de_tab_text(p,-1);

	/* calc number of characters in this line */
	if((text_monospaced > 0) || ((t->text_type > X_VIEW2) && (t->ext_edit)))
	{
		/* monospaced font, all characters are the same width, or external editor */
		if(t->text_type <= X_VIEW2)
			monospaced = text_monospaced;
		else
		{
			monospaced = text_monospaced_reply;

			if(t->ext_edit != 0)
				monospaced = 255;
			else if(t->wordwrap > 0)
				monospaced = t->wordwrap;
		}
		if(monospaced < 30)
			monospaced = 30;

		for(i=0; i<=monospaced; i++)
		{
			if(p[i] == '\n')
			{
				break;
			}
		}
		if(i > monospaced)
		{
			n_chars = monospaced;

			/* don't word-wrap in internet header */
			if(displ >= t->internet_header)
			{
				while(--n_chars > 0)
				{
					if(p[n_chars] <= ' ')
						break;   /* split character */
				}
				if(n_chars <= 0)
				{
					n_chars = monospaced;
				}
				else if(n_chars <= 5)
				{
					if(n_chars <= get_quote_string_length(p,&blanks)+blanks)
					{
						n_chars = monospaced;  /* no split character found */
					}
				}
			}
		}
		else
		{
			n_chars = i;
		}
		p_end = p + n_chars;
	}
	else
	{
		regs.r[0] = text_font;
		regs.r[1] = (int)p;
		regs.r[2] = 0x040120;   /* bits 5 (r5=scan block), 8 (r0=font handle), 18 (return bound.box) */
		regs.r[3] = (page_width-margin_l-margin_r) * scale_x;
		regs.r[4] = page_height * scale_y;
		regs.r[5] = (int)scan_block;

		os_swi(0x400a1, &regs);   /* Font_ScanString */
		p_end = (char *)regs.r[1];
		n_chars = p_end - p;

		if((displ < t->internet_header) || ((n_chars == 0) && (*p > ' ')))
		{
			/* no split point found, or in internet header */
			regs.r[1] = (int)p;
			regs.r[2] = 0x000180;   /* bits 7 (r7=length of string), 8 (r0=font handle) */
			regs.r[3] = (page_width-margin_l-margin_r) * scale_x;
			regs.r[4] = page_height * scale_y;
			regs.r[5] = 0;
			regs.r[7] = max_n_chars;

			os_swi(0x400a1, &regs);   /* Font_ScanString */
			p_end = (char *)regs.r[1];
			n_chars = p_end - p;
		}

		if((t->text_type > X_VIEW2) && (n_chars > t->wordwrap) && (t->wordwrap > 0) && (t->ext_edit==0))
		{
			/* restrict number of chars per line in replies */
			for(i = t->wordwrap; i > 0; i--)
			{
				if(p[i] <= ' ')
				{
					n_chars = i;
					p_end = p + n_chars;
					break;
				}
			}
		}
	}

	if(n_chars > max_n_chars)
		n_chars = max_n_chars;

	if(p[n_chars] <= ' ')
		n_chars++;

	if(n_chars == 0)
	{
		/* no split character found in specified width, search for split character */

		p = p_old;
		p_end = p + n_chars;

		while(++p_end < text_end)
		{
			if(*p_end <= ' ')
			{
				n_chars = p_end - p + 1;
				break;
			}
		}
		if(p_end >= text_end)
		{
			n_chars = text_end - p;
		}
	}
	*height = text_line_height;

	if(text_font < 0)
		*width = n_chars * 16;
	else
		*width = (scan_block[7] - scan_block[5]) / scale_x;
	return(n_chars);
}   /* end of scan_line */





void check_line_tab(TEXTR *t)
/***************************/
/* Restrict number of lines not to exceed the end of the text */
{
	int  count=0;
	int  line;
	int  n_chars;

	n_chars = t->text_body_end - t->text_start;

	for(line=0; line<t->n_lines; line++)
	{
		count += t->line_tab[line];
		if(count > n_chars)
		{
			t->n_lines = line;
			break;
		}
	}

}   /* end of check_line_tab */



void extend_line_tab(TEXTR *t, int *line)
/***************************************/
{
	char *p;

	if(*line >= t->n_line_tab)
	{
		p = realloc(t->line_tab,t->n_line_tab + N_LINE_TAB);
		if(p == NULL)
		{
			(*line)--;
		}
		else
		{
			t->line_tab = p;
			t->n_line_tab += N_LINE_TAB;
		}
	}
}   /* end of extend_line_tab */





void calc_line_tab(TEXTR *t)
/**************************/
/* Find the position of line breaks in the text, plus the height of each line */
{
	int  line;
	int  index;
	int  text_length;
	int  n_chars;
	int  height;
	int  width;

	visdelay2_begin();

	if(t->show_message_headers)
		t->text_start = 0;
	else
		t->text_start = t->internet_header2;

	line = 0;
	t->extent_y = TEXT_Y_OFFSET;
	index = t->text_start;
	text_length = t->text_body_end;

	while(index < text_length)
	{
		visdelay2_percent((index * 100)/text_length);

		n_chars = scan_line(t,index,&height,&width);

		t->line_tab[line] = n_chars;

		/*
		if((index + n_chars) > t->text_length)
		   t->line_tab[line] = t->text_length - index;
		*/
		t->extent_y += text_line_height;

		line++;
		extend_line_tab(t,&line);

		index += n_chars;
		if(t->text_base[index] == 0)
			break;
	}
	t->n_lines = line;
	visdelay2_end();

	/* set extent of work area */
	t->extent_y += text_line_height/4;
	set_text_extent(t);

}   /* end of calc_line_tab */




void text_reopen(TEXTR *t)
/**********************/
{
	reopen_window(t->w_card,0);
	reopen_window(t->w_text,0);
	reopen_window(t->w_attach,0);
	set_caret_pos(t);
}   /* text_reopen */







void redraw_text_lines(TEXTR *t, int linenum, int endline)
/********************************************************/
/* redraw from this line downwards */
{
	wimp_redrawstr r;
	int i;

	if(linenum < 0)  linenum = 0;

	if((endline >= 0) && (linenum > endline))
	{
		i = endline;
		endline = linenum;
		linenum = i;
	}


	/* give workarea coordinates of line to be redrawn */
	r.w = t->w_text;
	r.box.x0 = t->workarea.box.x0;
	r.box.x1 = t->workarea.box.x1;
	r.box.y1=  t->workarea.box.y1 - (linenum*text_line_height) - TEXT_Y_OFFSET;

	if(endline < 0)
		r.box.y0 = t->workarea.box.y0;
	else
		r.box.y0=  t->workarea.box.y1 - ((endline+1)*text_line_height) - TEXT_Y_OFFSET;

	wimp_force_redraw(&r);
}   /* end of redraw_text_lines */



void redraw_text_oneline(TEXTR *t,int line)
/*****************************************/
{
	redraw_text_lines(t,line,line);
}   /* end of redraw_text_oneline */



void redraw_text_partline(TEXTR *t, int line, int startx, int endx)
/*****************************************************************/
{
	int  i;
	wimp_redrawstr r;

	if(startx > endx)
	{
		/* swap so that startx <= endx */
		i = startx;
		startx = endx;
		endx = i;
	}


	r.w = t->w_text;
	r.box.x0 = startx - text_max_char_width;
	r.box.x1 = endx + text_max_char_width;
	r.box.y1 =  t->workarea.box.y1 - (line*text_line_height) - TEXT_Y_OFFSET;
	r.box.y0 =  r.box.y1 - text_line_height;

	wimp_force_redraw(&r);
}   /* end of redraw_text_partline */



int recalc_line_tab(TEXTR *t, int start_line, int chars_added)
/************************************************************/
/* Recalculate the line_tab after characters have been added / deleted
   from a line.  Also redraws the window */
{
	int  old_char_count=0;
	int  new_char_count=0;
	int  line;
	int  n_chars;
	int  height;
	int  width;
	int  index;
	int  text_length;
	int  x;
	int  i;
	int  dy = 0;    /* change in cursor y coordinate */
	int  prev_extent_y;

	prev_extent_y = t->extent_y;

	t->line_tab[start_line] += chars_added;
	index = t->cursor_line_start;
	line = start_line;
	text_length = t->text_body_end;


	if(line > 0)
	{
		line--;
		index -= t->line_tab[line];
	}


	if((t->attachments > 0) && (t->attach[0].displ < text_length))
	{
		text_length = t->attach[0].displ;
	}

	while(index < text_length)
	{
		n_chars = scan_line(t,index,&height,&width);

		new_char_count += n_chars;

		if(new_char_count == old_char_count)
		{
			/* equal, but with an extra line added, firstly move line_tab down */
			for(i=t->n_lines; i>line; i--)
			{
				t->line_tab[i] = t->line_tab[i-1];
				/*            text_line_height = text_line_height; */
			}
			t->n_lines++;   /* should check for data overflow here !!!! */
			extend_line_tab(t,&t->n_lines);

			t->line_tab[line] = n_chars;
			/*         text_line_height = height; */

			if(line >= t->n_lines)
				t->n_lines = line+1;
			else
				line = t->n_lines-1;   /* force redraw whole screen - better to scroll ? */
			t->extent_y += text_line_height;
			break;
		}

		if(line < t->n_lines)
			old_char_count += t->line_tab[line];

		t->line_tab[line] = n_chars;

		/*
		if((index + n_chars) > t->text_length)
		   t->line_tab[line] = t->text_length - index;
		*/

		/*      text_line_height = height; */
		index += n_chars;

		if((new_char_count == old_char_count) && (line >= start_line))   /* was line > start_line */
			break;

		if(t->text_base[index] == 0)
			break;

		line++;
		extend_line_tab(t,&line);
	}

	/****** ADJUST EXTENT_Y !!! **********/

	if(line > t->n_lines)
		t->n_lines = line;

	extend_line_tab(t,&t->n_lines);

	start_line--;   /* could affect the previous lie, unless there is a space between the
                      start of this line and the edit point */

	for(x = t->cursor_line_start; x < (t->cursor_index-1); x++)
	{
		if(t->text_base[x] <= ' ')
		{
			start_line++;
			break;
		}
	}

	redraw_text_lines(t,start_line,line);

	x = t->cursor_index - t->cursor_line_start;
	if(x >= t->line_tab[t->cursor_line])
	{
		/* cursor has moved to the next line */
		t->cursor_line_start += t->line_tab[t->cursor_line];
		t->cursor_line++;
		dy = -text_line_height;
	}
	else if(x < 0)
	{
		/* cursor has moved to the previous line */
		dy = text_line_height;
		t->cursor_line--;
		t->cursor_line_start -= t->line_tab[t->cursor_line];
	}

	if(t->cursor_line_start < 0)
		t->cursor_line_start = 0;

	t->extent_y = t->n_lines * text_line_height;
	t->extent_y += (TEXT_Y_OFFSET + text_line_height/4);

	if(prev_extent_y != t->extent_y)
	{
		set_text_extent(t);
	}

	check_line_tab(t);
	return(dy);
}   /* end of recalc_line_tab */




void text_recalc_delete(TEXTR *t, int start_index, int chars_added)
/************************************************************/
/* Recalculate the line_tab after characters have been added / deleted
   from a line.  Also redraws the window */
{
	int  old_count=0;
	int  new_count=0;
	int  start_line;
	int  line;
	int  deleted_lines=0;
	int  n_chars;
	int  height;
	int  width;
	int  index;
	int  i;
	int  finished=0;
	int  prev_extent_y;
	wimp_caretstr caretstr;
	wimp_wstate wstate;

	prev_extent_y = t->extent_y;

	/* find start line */
	start_line=0;
	i = t->text_start;
	while((i + t->line_tab[start_line]) <=start_index)
	{
		i += t->line_tab[start_line];
		start_line++;
	}
	index = i;    /* start of start line */

	line = start_line;

	if(index >= t->text_body_end)
	{
		deleted_lines = t->n_lines - start_line - 1;
	}

	old_count = chars_added;
	while((index < t->text_body_end) && (!finished))
	{
		n_chars = scan_line(t,index,&height,&width);

		new_count += n_chars;
		old_count += t->line_tab[line];

		t->line_tab[line] = n_chars;
		/*      text_line_height = height; */
		index += n_chars;

		deleted_lines = 0;
		if(new_count == old_count)
			break;

		i = old_count;
		while(i <= new_count)
		{
			if(i == new_count)
			{
				finished = 1;
				break;
			}
			deleted_lines++;
			i += t->line_tab[line + deleted_lines];
		}

		if(t->text_base[index] == 0)
			break;    /* end of text */

		if(!finished)
		{
			line++;
			extend_line_tab(t,&line);
		}
	}

	if(deleted_lines > 0)
	{
		for(i=0; i<deleted_lines; i++)
		{
			t->extent_y -= text_line_height;
		}
		for(i=line+1; i<(t->n_lines-deleted_lines); i++)
		{
			t->line_tab[i] = t->line_tab[i+deleted_lines];
			/*         text_line_height = text_line_height;  */
		}
		t->n_lines -= deleted_lines;

		line++;
		extend_line_tab(t,&line);
	}

	/****** ADJUST EXTENT_Y !!! **********/

	if(line > t->n_lines)
		t->n_lines = line;

	if(deleted_lines)
		line = t->n_lines;     /* redraw whole screen */




	if(prev_extent_y != t->extent_y)
	{
		set_text_extent(t);

		if(t->attachments)
		{
			wimp_get_wind_state(t->w_text,&wstate);
			open_article_window(t,&wstate.o,0);
		}
		text_reopen(t);
	}

	check_line_tab(t);

	if((start_line==line) && (chars_added == -1))
	{
		wimp_get_caret_pos(&caretstr);
		redraw_text_partline(t,line,caretstr.x,page_width);
	}
	else
	{
		redraw_text_lines(t,start_line,line);
	}
}   /* end of text_recalc_delete */




void replace_control_chars(TEXTR *t, int start, int length)
/*********************************************************/
/* Replace illegal control characters in the specified area
   by newlines, to stop font handler crashing */
{
#ifdef deleted
	int  c;
	char *p;
	char *p_end;

	/* this would defeat YENC decoding */

	p = &t->text_base[start];
	p_end = &p[length];


	while(p < p_end)
	{
		if(*p < ' ')
		{
			if(((c = *p) == '\n') || (c == '\t'))
			{
			}
			else if(c == 12)
			{
				/* formfeed */
				*p = '\n';
			}
			else
			{
				/*            *p = '#'; */
			}
		}
		p++;
	}
#endif

}   /* end of replace_control_chars */






int move_down(TEXTR *t, int start, int by)
/****************************************/
/* Move the contents of the edit buffer down to leave a gap */
{
	int  n_chars;   /* no. of chars. to move */
	char *p;
	char *p2;
	int  size;
	int  i;
	int *pi;

	size = t->text_length + by + 8;   /* safety margin in case of errors */
	if(size >= t->text_buf_size)
	{
		/* need to allocate more data */
		size = size + TEXT_EXTRA;
		if(flex_extend((flex_ptr)&t->text_base,size) == 0)
		{
			werr(FALSE,"Can't allocate memory");
			return(1);
		}
		t->text_buf_size = size;
	}

	if((t->sig_start < t->text_length) && (t->sig_start > start))
		t->sig_start += by;

	if(t->text_body_end >= start)
		t->text_body_end += by;


	pi = &t->text_start; /* this and the next 5 words in TEXTR */
	for(i=0; i<7; i++)
	{
		if(pi[i] > start)
			pi[i] += by;
	}

	for(i=0; i<t->attachments; i++)
	{
		if(t->attach[i].displ >= start)
			t->attach[i].displ += by;
	}

	n_chars = t->text_length - start + 1;    /* include terminator character at ebuf_end */
	t->text_length += by;

	p2 = &t->text_base[start];

	if(by == 1)
	{
		/* optimised assembler routine for move down by 1 byte */
		move_down_1(p2,n_chars);
	}
	else
	{
		p = &t->text_base[start] + by;

		while(--n_chars >= 0)
		{
			p[n_chars] = p2[n_chars];
		}
	}
	return(0);
}   /* end of move_down */





void move_up(TEXTR *t, int start, int by)
/***************************************/
/* Move part of the edit buffer up to delete characters */
{
	int  i;
	char *p;
	char *p2;
	int  *pi;
	int  n_chars;
	int  displ;

	if(by == 0)
		return;

	if(by > start)
		by = start;

	displ = start - by;

	pi = &t->text_start; /* this and the next 8 words in TEXTR */
	for(i=0; i<9; i++)
	{
		if(pi[i] >= start)
			pi[i] -= by;
		else if(pi[i] > displ)
			pi[i] = displ;
	}

	for(i=0; i<t->attachments; i++)
	{
		if(t->attach[i].displ >= start)
			t->attach[i].displ -= by;
	}

	n_chars = t->text_length - start + 1;
	t->text_length -= by;

	p2 = &t->text_base[start];

	if(by == 1)
	{
		/* optimised routine to move block up by one byte */
		move_up_1(p2,n_chars);
	}
	else
	{
		p = &t->text_base[start - by];

		for(i=0; i<n_chars; i++)
		{
			p[i] = p2[i];
		}
	}

	t->text_base[t->text_length] = 0;
}   /* end of move_up */



void text_window_title(TEXTR *t)
/******************************/
{
	char *string1;
	char *changed;
	int  box;
	FOLDREC *fr;
	char counts[20];
	char buf[120];

	if(t->text_type <= X_VIEW2)
	{
		if(t->fr != NULL)
			box = t->fr->box;

		if(t->text_type == X_VIEW)
			string1 = "Article Viewer";
		else
			string1 = "Additional Viewer";

		if(t->changed)
			changed = "*";
		else
			changed = "";

		fr = t->fr;

		if((box >= 0) && (box < 255))
			sprintf(counts," %d / %d",boxes_unread_count[box],boxes_count[box]);
		else
			strcpy(counts,"");

		if(t->level_index > 0)
			sprintf(buf,"%d of %d   %s %s %s",t->level_index,t->level_total,string1,changed,counts);
		else
			sprintf(buf,"%s %s",string1,changed);
		buf[79]=0;
		win_settitle(t->w_card,buf);
	}
}   /* end of text_window_title */




void mark_changed(TEXTR *t)
/*************************/
{
	if(t->changed != 1)
	{
		t->changed = 1;
		text_window_title(t);

		if(t->text_type == X_NEWS)
			wimp_set_icon_state(t->w_card,1,0,wimp_INOSELECT);   /* unfade Post button */
	}
}   /* end of mark_changed */



void mark_changed2(TEXTR *t)
/**************************/
/* Allow Save but don't warn on discard */
{
	if(t->changed==0)
		t->changed = 2;
}   /* end of mark_changed2 */



void clear_text_region(TEXTR *t)
/******************************/
{
	int  i;
	int  start_line;
	int  end_line;

	for(i=0; i<N_TEXT_DATA; i++)
	{
		t = text_data_record[i];
		if(t == NULL)
			continue;

		if(t->region_end > t->region_start)
		{
			index_to_linestart(t,t->region_start,&start_line);
			index_to_linestart(t,t->region_end,&end_line);
			redraw_text_lines(t,start_line,end_line);

			t->region_start=0;
			t->region_end=0;
		}
	}
}   /* end of clear_text_region */




void paste_clipboard(TEXTR *t)
/****************************/
/* Request contents of global clipboard */
{
	wimp_msgstr m;

	if(clipboard != NULL)
	{
		/* we have something on the clipboard */

		if(move_down(t,t->cursor_index,clipboard_size) == 0)
		{
			memcpy(&t->text_base[t->cursor_index],clipboard,clipboard_size);
		}
		calc_line_tab(t);
		redraw_text_lines(t,0,-1);
		mark_changed(t);

		t->cursor_index += clipboard_size;
		set_caret_pos(t);
	}
	else if(options.edit_style != EDIT_EDIT)
	{
		/* request a DataSave from the global clipboard */

		m.hdr.size = 48;
		m.hdr.your_ref = 0;
		m.hdr.action = 16;   /* Message_DataRequest */
		m.data.words[0] = (int)t->w_text;
		m.data.words[1] = (int)t;
		m.data.words[2] = 0;
		m.data.words[3] = 0;
		m.data.words[4] = 4;  /* set bit 2 */
		m.data.words[5] = 0xfff;
		m.data.words[6] = -1;

		wimp_sendmessage(wimp_ESEND,&m,0);
	}
}   /* end of paste_clipboard */




void paste_clipboard_dbox(dbox d, int field)
/**************************************/
/* Request contents of global clipboard */
{
	int  i;
	int  length;
	int  cursor=0;
	int  start;
	int  end;
	int  size;
	int  import;
	char *filename;
	FILE *f;
	wimp_msgstr m;
	wimp_caretstr caretstr;
	char buf[256];

	if(field < 0)
		return;

	size = clipboard_size;

	if((field >> 16) == 1)
		import = 1;
	else
		import = 0;
	field &= 0xffff;

	wimp_get_caret_pos(&caretstr);
	if((caretstr.w == dbox_syshandle(d)) && (caretstr.i == field))
		cursor = caretstr.index;

	if(import)
	{
		xferrecv_checkinsert(&filename);
		f = fopen(filename,"r");
		if(f==NULL)
			return;

		size = get_filelength(filename);
	}


#ifdef deleted
	if((clipboard != NULL) || (import))
#endif
		if(import)
		{
			/* we have something on the clipboard */
			dbox_getfield(d,field,buf,sizeof(buf));
			length = strlen(buf);

			end = length+size;
			start = cursor+size;

			if(end >= sizeof(buf))
				end = sizeof(buf)-1;

			for(i=end; i>=start; i--)
			{
				buf[i] = buf[i-size];
			}
			length = size;
			if((cursor+length) >= sizeof(buf))
			{
				length = sizeof(buf) - cursor - 1;
			}

			if(import)
			{
				fread(&buf[cursor],1,length,f);
				fclose(f);
				xferrecv_insertfileok();
			}
			else
			{
				memcpy(&buf[cursor],clipboard,length);
			}
			dbox_setfield(d,field,buf);
		}
		else if(options.edit_style != EDIT_EDIT)
		{
			/* request a DataSave from the global clipboard */

			m.hdr.size = 48;
			m.hdr.your_ref = 0;
			m.hdr.action = 16;   /* Message_DataRequest */
			m.data.words[0] = (int)dbox_syshandle(d);
			m.data.words[1] = field + 0x10000;
			m.data.words[2] = 0;
			m.data.words[3] = 0;
			m.data.words[4] = 4;  /* set bit 2 */
			m.data.words[5] = 0xfff;
			m.data.words[6] = -1;

			wimp_sendmessage(wimp_ESEND,&m,0);
		}
}   /* end of paste_clipboard_dbox */



void clipboard_copy_data(char **anchor, int start, int length)
/******************************************************/
{
	wimp_msgstr m;

	/* copy to clipboard */
	if(clipboard != NULL)
		flex_free((flex_ptr)&clipboard);

	if(options.edit_style != EDIT_EDIT)
	{
		/* claim the global clipboard */
		m.hdr.size = 24;
		m.hdr.your_ref = 0;
		m.hdr.action = 15;   /* Message_ClaimEntity */
		m.data.words[0] = 4;   /* bit 2 - global clipboard */

		wimp_sendmessage(wimp_ESEND,&m,0);
	}

	if(flex_alloc((flex_ptr)&clipboard,length) != 0)
	{
		memcpy(clipboard,*anchor+start,length);
		clipboard_size = length;
	}
}   /* end of clipboard_copy_data */





void region_copy(TEXTR *t_dst, int move, int edit_style)
/******************************************************/
/* Copy or move region to cursor position */
{
	TEXTR *t_src;
	int  region_size;

	t_src = t_dst;
	if(edit_style == EDIT_EDIT)
	{
		/* find the text window which has the selected region */
		t_src = t_region;
	}

	if(t_src->region_end == 0)
	{
		beep();
		return;    /* no region marked */
	}

	region_size = t_src->region_end - t_src->region_start;


	/* copy to clipboard */
	clipboard_copy_data(&t_src->text_base,t_src->region_start,region_size);

	if(move)
	{
		/* delete origional */
		move_up(t_src,t_src->region_end,region_size);
		calc_line_tab(t_src);

		if(t_src->cursor_index > t_src->region_end)
		{
			t_src->cursor_index -= region_size;
			set_caret_pos(t_src);
		}
		t_src->region_end = 0;
		redraw_text_lines(t_src,0,-1);
		mark_changed(t_src);
	}

	if(edit_style == EDIT_EDIT)
	{
		paste_clipboard(t_dst);
	}
}   /* end of region_copy */




void insert_text_file(TEXTR *t)
/*****************************/
/* Insert contents of text file at cursor position */
{
	char *p;
	int  i;
	int  length;
	FILE *f_input;
	int  filetype;
	char *fname;
	char filename[256];

	if(t->allow_edit == 0)
	{
		beep();  /* edit now allowed */
		return;
	}


	filetype = xferrecv_checkinsert(&fname);
	strcpy(filename,fname);

	if(akbd_pollsh() && (memcmp_lc(filename,"<wimp$scrap",11) != 0))
	{
		/* shift key is held down, paste full pathname into the text */
		length = strlen(filename);
		if(move_down(t,t->cursor_index,length) == 0)
		{
			memcpy(&t->text_base[t->cursor_index],filename,length);
		}
	}
	else
	{
		length = get_filelength(filename);

		if((filetype != 0xfff) && (filetype != 0xb28))
		{
			/* allow text, url, */
			if(query("Insert non-text file into the text window ? (Drop onto the header bar to add as an attachment)","OK")==0)
				return;
		}
		else if(length > 10000)
		{
			if(query("This is rather a long file to insert into the text. (Drop onto the header bar to add as an attachment)","Insert")==0)
				return;
		}

		f_input = fopen_werr(filename,"r","");
		if(f_input == NULL)
		{
			return;
		}

		if(move_down(t,t->cursor_index,length) == 0)
		{
			p = &t->text_base[t->cursor_index];
			for(i=0; i<length; i++)
			{
				p[i] = fgetc(f_input);
			}
		}
		fclose(f_input);
	}

	if(strcmp_lc(filename,"<wimp$scrap>") == 0)
	{
		/* delete input file if it was <Wimp$Scrap> */
		os_swi4(0x29, 27, (int)filename, 0, 0);       /* OS_FSControl  Wipe */
	}

	xferrecv_insertfileok();

	replace_control_chars(t,t->cursor_index,length);
	calc_line_tab(t);
	redraw_text_lines(t,t->cursor_line,-1);

	mark_changed(t);

	t->cursor_index += length;
	set_caret_pos(t);
}   /* end of insert_text_file */




void text_delete_line(TEXTR *t)
/****************************/
{
	int  region_size;
	int  line_end;

	region_size = t->line_tab[t->cursor_line];
	line_end = t->cursor_line_start + region_size;

	if(line_end >= t->text_body_end)
	{
		line_end = t->text_body_end - 1;
		region_size = line_end - t->cursor_line_start;
	}

	/* adjust marked region if there is one */
	if((t->region_start > t->cursor_line_start) && (t->region_start <= line_end))
		t->region_start = t->cursor_line_start;

	if((t->region_end > t->cursor_line_start) && (t->region_end <= line_end))
		t->region_end = t->cursor_line_start;

	if(t->region_start == t->region_end)
		t->region_start = t->region_end = 0;

	t->cursor_index = t->cursor_line_start;
	move_up(t, line_end, region_size);

	text_recalc_delete(t, t->cursor_line_start,-region_size);

	set_caret_pos(t);
	redraw_text_lines(t,0,-1);
}  /* end of text_delete_line */



void text_select_line(TEXTR *t)
/*****************************/
{
	clear_text_region(t);

	t->region_start = t->cursor_line_start;
	t->region_end = t->region_start + t->line_tab[t->cursor_line];
	redraw_text_oneline(t,t->cursor_line);

}   /* end of text_select_line */




void text_highlight_cursor_word(TEXTR *t)
/***********************************/
{
	int start, end;
	int c;
	char *p;

	clear_text_region(t);
	p = t->text_base;

	for(start=t->cursor_index; start>=0; start--)
	{
		if(!isalnum(c = p[start]))
		{
			if((c != '\'') || (start == 0) || (!isalnum(p[start-1])))
				break;
		}
	}
	start++;

	for(end=start; end<t->text_body_end; end++)
	{
		if(!isalnum(c = p[end]))
		{
			if((c != '\'') || (end >= (t->text_body_end-1)) || (!isalpha(p[end+1])))
				break;
		}
	}

	t->region_start = start;
	t->region_end = end;
	cursor_to_warea(t,&start,&end);  /* don't need x,y result */
	redraw_text_oneline(t,t->cursor_line);
}   /* text_highlight_cursor_word */




void text_highlight_string(TEXTR *t, int length)
/**********************************************/
{
	if(length > 0)
	{
		clear_text_region(t);
		t->region_start = t->cursor_index;
		t->region_end = t->region_start + length;
		set_caret_pos(t);
		redraw_text_lines(t,t->cursor_line,t->cursor_line+1);
	}
}   /* end of text_highlight_string */





int text_find_string_next(TEXTR *t, char *string, int case_sensitive, int regex_flag)
/***********************************************************************************/
/* Find next occurance of string in the text */
{
	int  index;
	int  start;
	int  string_length;

	string_length = strlen(string);

	start = t->cursor_index + 1;


	index = search_regex(string,string_length,&t->text_base,start,
						 t->text_body_end-start,case_sensitive,regex_flag,&string_length);

	if(index >= 0)
	{
		t->cursor_index = index;
		set_caret_pos(t);
		text_highlight_string(t,string_length);
	}
	else
	{
		return(-1);
	}
	set_caret_pos(t);
	return(index);
}   /* end of text_find_string_next */



void text_find_string(TEXTR *t)
/*****************************/
{
	dbox d;
	int  ix;
	int  action;
	int  index;
	int  string_length;

	d = dbox_new("Find");
	dbox_raw_eventhandler(d,dbox_help_handler,(void *)HELP_FIND);

	dbox_setfield(d,2,search_text_string);
	dbox_setnumeric(d,3,search_case_sensitive);
	dbox_setnumeric(d,4,search_regex_flag);
	dbox_show(d);
	action = dbox_fillin(d);

	switch(action)
	{
	case 0:
		search_case_sensitive = dbox_getnumeric(d,3);
		search_regex_flag = dbox_getnumeric(d,4);
		if(search_regex_flag) search_regex_flag = -1;

		dbox_getfield(d,2,search_text_string,sizeof(search_text_string)-1);

		search_length_str = strlen(search_text_string);
		if((!search_case_sensitive) && (search_regex_flag == 0))
		{
			/* convert string to lower case, and remove accents */
			for(ix=0; ix<=search_length_str; ix++)
				search_text_string[ix] = chars_lc[search_text_string[ix]];
		}

		string_length = search_length_str;

		index = search_regex(search_text_string,search_length_str,&t->text_base,t->cursor_index,
							 t->text_body_end-t->cursor_index,search_case_sensitive,search_regex_flag,&string_length);

		if(index >= 0)
		{
			t->cursor_index = index;
			text_highlight_string(t,string_length);
		}
		else
		{
			beep();
		}
		break;
	}

	dbox_dispose(&d);
	set_caret_pos(t);
}   /* end of text_find_string */





int width_mono_string(char *string, int n_chars)
/**********************************************/
{
	int  i;
	int  count=0;

	/* count printable characters */
	for(i=0; i<n_chars; i++)
	{
		if(string[i] == 19)
		{
			i+=7;
		}
		else
		{
			count++;
		}
	}

	return((count * text_monospaced_width) / scale_x);
}   /* end of width_mono_string */




void text_rubout(TEXTR *t,char *buf,int *start,int *end)
/******************************************************/
{
	int  x1, x2;
	os_regset regs;

	if(*start == 0)
	{
		x1 = 0;
	}
	else
	{
		if(text_monospaced)
		{
			x1 = width_mono_string(buf,*start);
		}
		else
		{
			regs.r[0] = text_font;
			regs.r[1] = (int)buf;
			regs.r[2] = 0x180;   /* bit 8 */
			regs.r[3] = 0x7fffffff;
			regs.r[4] = 0x7fffffff;
			regs.r[7] = *start;
			os_swi(0x400a1,&regs);   /* Font_ScanString */

			x1 = regs.r[3] / scale_x;
		}
	}

	if(*end == 0x1000)
	{
		x2 = page_width;
	}
	else
	{
		if(text_monospaced)
		{
			x2 = width_mono_string(buf,*end);
		}
		else
		{
			regs.r[0] = text_font;
			regs.r[1] = (int)buf;
			regs.r[2] = 0x180;   /* bit 8 */
			regs.r[3] = 0x7fffffff;
			regs.r[4] = 0x7fffffff;
			regs.r[7] = *end;
			os_swi(0x400a1,&regs);         /* Font_ScanString */

			x2 = regs.r[3] / scale_x;
		}
	}

	*start = x1;
	*end = x2;
}   /* end of text_rubout */



int is_quoted_text2(char *p, int *n_chars)
/****************************************/
{
	int  c;
	int  count=0;
	int  special_quote;
	char *p_first = p;
	char *p_start = p;


	if(p[0] == ']')
		special_quote = 1;
	else
		special_quote = 0;

	for(;;)
	{
		if(((c = p[0]) == '>') || (c == ']') || (c == '}'))
		{
			count++;
			p++;
		}
		else if((c == ':') && (p[1] == ' '))
		{
			count++;
			p++;
		}
		else if(c == '|')
		{
			if((p[1] == '-') && (p[2] != ' ') && !isalnum(p[2]))
				break;

			count++;
			p++;
		}
		else if((p[2] == '>') && isalpha(c) && isalpha(p[1]))
		{
			count++;
			p+=3;
		}
		else
		{
			break;
		}

		if(*p == ' ')
			p++;

		if(count==1)
			p_first = p;
	}

	if((count == 1) && (special_quote))
		count = 4;

	if(count > 4) count = 4;
	/*   *n_chars = p - p_start; */
	*n_chars = p_first - p_start;    /* num of chars in first level of quoting */
	return(count);
}   /* end of is_quoted_text2 */




int is_quoted_text(char *p)
/***********************/
{
	int n_chars;

	return(is_quoted_text2(p,&n_chars));
}   /* end of is_quoted_text */




int display_text_lines(TEXTR *t, int start_line, int num_lines, int x, int y)
/****************************************************************************/
{
	int  count;
	int  ix, ix2;   /* start and end of line */
	int  i;
	int  c;
	char *p;
	int  n_chars;
	int  r1, r2;    /* start and end of region */
	int  region_start, region_end;  /* char ix into this line */
	int  colr_word;
	char *main_text_colr;
	char *current_colr;
	char *previous_text_colr;
	int  highlighted=0;
	int  line_highlighted=0;
	int  xpos;
	int  tab_spaces;
	int  offset;
	int  text_font2;   /* modified by character set if appropriate */
	int  colr_header = COLR_HEADER;

	int  j;
	char *p2;
	int  addr_start1;
	int  addr_start2;
	int  addr_start3;
	int  addr_start4;
	int  addr_highlight;
	int  font_opt;
	MNEM_TAB2 *m;

	os_regset regs;
	char buf[512];

	if((pointer_sanity_check(1,t) < 0) || (pointer_sanity_check(2,t->text_base) < 0))
	{
		text_close_window(t);
		return(-1);
	}

	if(start_line >= t->n_lines)
		return(0);

	/* find the font for the required encoding variant (eg. Latin3) */
	font_opt = FONT_TEXT + (options.text_font_toggle & 1);
	text_font2 = font_find_variant(fontname[font_opt],t->charset,
								   options.d.font[font_opt].width,options.d.font[font_opt].height);

	if(text_font2 == 0)
		text_font2 = text_font;


	r1 = t->region_start;
	r2 = t->region_end;

	y -= text_font_offset;
	x += margin_l;

	ix = t->text_start;
	for(i=0; i<start_line; i++)
	{
		ix += t->line_tab[i];
	}

	while((start_line > 0) &&
			((t->text_base[ix-1] != '\n') || ((ix < t->internet_header) && (isspace(t->text_base[ix])))))
	{
		/* starting with a wordwrapped line, or a wrapped internet header line,
		   go back one line */
		start_line--;
		ix -= t->line_tab[start_line];
		y += text_line_height;
		num_lines++;
	}

	main_text_colr = colr_text[COLR_TEXT];
	previous_text_colr = main_text_colr;

	p = buf;
	if((ix >= r1) && (ix < r2))
	{
		/* start off highlighted */
		memcpy(p,colr_inverse,8);
		p += 8;
		highlighted = 1;
		region_start = 0;
		region_end = 0x1000;
	}
	else
	{
		region_start = -1;
	}

	current_colr = NULL;

	for(count=0; (count<num_lines) && (start_line < t->n_lines); count++)
	{
		line_highlighted = highlighted;

		n_chars = t->line_tab[start_line];
		ix2 = ix + n_chars;


		main_text_colr =  colr_text[COLR_TEXT];

		if(ix < t->internet_header)
		{
			p2 = &t->text_base[ix];
			if((ix==0) || (!isspace(p2[0]) && (p2[-1] == '\n')))
			{
				colr_header = COLR_HEADER;

				m = header_lines;
				while(m->mnem != NULL)
				{
					if(memcmp_lc(p2,m->mnem,m->len) == 0)
					{
						if(m->value > 0)
							colr_header = COLR_HEADER2;
						break;
					}
					m++;
				}
			}

			main_text_colr = colr_text[colr_header];
		}
		else if(ix >= t->sig_start)
		{
			main_text_colr = colr_text[COLR_SIG];
		}
		else if((t->text_type <= X_VIEW2) && (ix > t->text_start) && (t->text_base[ix-1] != '\n'))
		{
			/* colour a wordwrap continuation the same colour as the previous line */
			main_text_colr = previous_text_colr;
		}
		else
		{
			if((i = is_quoted_text(&t->text_base[ix])) > 0)
			{
				main_text_colr = colr_text[COLR_QUOTE-1+i];
			}
		}
		previous_text_colr = main_text_colr;


		if((highlighted == 0) && (current_colr != main_text_colr))
		{
			memcpy(p,main_text_colr,8);
			p += 8;
			current_colr = main_text_colr;
		}

		if((ix >= r1) && (ix < r2))
		{
			region_start = 0;
			region_end = 0x1000;
		}
		else
		{
			region_start = -1;
		}

		/* look for email addressea and URL's to colour red */
		i = ix;
		addr_start1 = -1;
		p2 = t->text_base;

		if(i >= t->internet_header)
		{
			while(i < ix2)
			{
				if((i > 0) &&
						((p2[i] == '@') && isalnum(p2[i+1]) && !isspace(p2[i-1]))
						|| ((p2[i] == ':') && (p2[i+1] == '/') && ((p2[i+2] == '/') || (memcmp(&p2[i-4],"file",4)==0)))
						|| ((p2[i] == '.') && (memcmp(&p2[i-3],"www",3)==0) && (!isalnum(p2[i-4])) ))
				{
					if(isalnum(p2[i-1]) || (strchr("",p2[i-1])==NULL))
					{
						j = i;
						while(--j >= ix)
						{
							if((p2[j] <= ' ') || (strchr("\"([{>)]}",p2[j])!=NULL))
							{
								break;
							}
						}

						if(addr_start1 == -1)
						{
							addr_start1 = j+1;
							addr_start2 = addr_start3 = addr_start4 = -1;
						}
						else if(addr_start2 == -1)
						{
							addr_start2 = j+1;
						}
						else if(addr_start3 == -1)
						{
							addr_start3 = j+1;
						}
						else
							addr_start4 = j+1;
					}
				}
				i++;
			}
		}

		i = ix;

		/* write out one line */
		xpos = 0;
		while(i < ix2)
		{
			if(addr_start1 >= 0)
			{
				if((i == addr_start1) || (i == addr_start2) || (i == addr_start3) || (i == addr_start4))
				{
					if(highlighted == 0)
					{
						memcpy(p,colr_text[COLR_URL],8);
						p+=8;
						addr_highlight = 1;
					}
				}
			}

			if((i == r1) && (i < r2))
			{
				region_start = p - buf;
				region_end = 0x1000;
				memcpy(p,colr_inverse,8);
				highlighted = 1;
				p+=8;
			}
			else if((i == r2) && (i > 0))
			{
				region_end = p - buf;
				highlighted = 0;

				memcpy(p,main_text_colr,8);
				p+=8;
				current_colr = main_text_colr;
			}

			c = t->text_base[i++];
			if((addr_highlight) && (highlighted==0))
			{
				if((c <= ' ') || (strchr("\")]",c)!=NULL))
				{
					memcpy(p,main_text_colr,8);
					p+=8;
					addr_highlight = 0;
				}
			}

			if(c == '\t')
			{
				tab_spaces = ((xpos + 8) & ~7) - xpos;
				tab_spaces = 1;
				memset(p,' ',tab_spaces);
				p += tab_spaces;
				xpos += tab_spaces;
			}
			else
			{
				if((c == '\n') || (c == '\f'))
					c = '\n';
				else if(c < ' ')
					c = '#';
				*p++ = c;
				xpos++;
			}
		}
		*p = 0;

		/* plot background rectangle */
		if(region_start >= 0)
		{
			text_rubout(t,buf,&region_start,&region_end);
			os_swi6(0x40743,stripe_backg,0,0,0,0,0);  /* SetGCOL - black */

			offset = text_font_offset-text_line_height;
			/*         bbc_rectanglefill(x+region_start,y+text_font_offset-text_line_height,
			                                        region_end-region_start,text_line_height-2); */
			bbc_rectanglefill(x+region_start,y+offset,
							  region_end-region_start,text_line_height-2);

			if((region_start > 0) && (text_font2 < 0))
			{
				/* system font, and region starts later in this line.
				   Make sure the text colour is set correctly. */
				p = &main_text_colr[1];
				colr_word = (p[3] << 8) + (p[4] << 16) + (p[5] << 24);
				set_colour(colr_word,0,0);
			}
		}


		if(text_font2 >= 0)
		{
			regs.r[0] = text_font2;
			regs.r[1] = (int)buf;
			regs.r[2] = 0x190;    /* bit 4, bit 7, bit 8 */
			regs.r[3] = x;
			regs.r[4] = y;
			regs.r[7] = p-buf;
			os_swix(0x40086,&regs);      /* Font_Paint */
		}
		else
		{
			/* system font */
			bbc_move(x, y-2);

			if(line_highlighted)
			{
				set_colour(stripe_foreg,0,0);
			}
			p = buf;
			while((c = *p++) != 0)
			{
				if(c == 19)
				{
					colr_word = (p[3] << 8) + (p[4] << 16) + (p[5] << 24);
					set_colour(colr_word,0,0);
					p+=7;
				}
				else
				{
					bbc_vdu(c);
				}
			}

		}

		ix = ix2;
		y -= text_line_height;
		start_line++;
		p = buf;
	}
	return(0);
}   /* end of display_text_lines */






int display_text_window(TEXTR *t, wimp_redrawstr *r)
/**************************************************/
{
	int  min_x, min_y;
	int  max_x, max_y;
	int  line;
	int  y;
	int  end_line;
	int  backg = -1;

	min_x = r->g.x0 - r->box.x0 + r->scx;
	max_x = r->g.x1 - r->box.x0 + r->scx;

	min_y = (r->box.y1 - r->g.y1) - r->scy;
	max_y = (r->box.y1 - r->g.y0) - r->scy;

	if(colour_backg_fill==255)
	{
		backg = colour_backg;
	}
	if(t->ext_edit)
	{
		backg = (int)0xaaaaaa00;
	}
	if(backg != -1)
	{
		os_swi6(0x40743,backg,0,0,0,0,0);  /* SetGCOL - mid grey */
		bbc_rectanglefill(r->g.x0,r->g.y0,r->g.x1-r->g.x0, r->g.y1-r->g.y0);
	}

	if(t->ext_edit)
		return(0);

	line = (min_y - TEXT_Y_OFFSET)/ text_line_height;
	end_line = (max_y - TEXT_Y_OFFSET) / text_line_height;
	if(line < 0) line = 0;

	y = line * text_line_height + TEXT_Y_OFFSET;

	if(display_text_lines(t,line,end_line - line + 1,r->box.x0 - r->scx, r->box.y1 - r->scy - y) < 0)
		return(-1);

	return(0);
}   /* display_text_window */






int coords_to_index(TEXTR *t, wimp_wstate *wstate, int screen_x, int screen_y, int *out_line, int *out_line_start)
/******************************************************************************************************/
/* Convert screen coords to an index into the text */
{
	int  line;                  /* line within the text */
	int  i;
	int  x;
	int  y;
	int  index;
	int  height;
	int  width;
	char *p;
	char *p1;
	int  padding=0;
	int  offset=0;
	os_regset regs;

	static int coord_block[9] = {0,0,0,0,-1,0,0,0,0};

	i = wstate->o.box.y1 - screen_y;   /* displacement down the window */

	line = warea_to_line(t,wstate->o.y - i,&y,&index,text_line_height);
	height = text_line_height;
	p = &t->text_base[index];
	p1 = de_tab_text(p, t->line_tab[line]);

	if(out_line != NULL)
		*out_line = line;

	if(out_line_start != NULL)
		*out_line_start = index;

	/* find character at cursor, and its x position */
	width = (screen_x - wstate->o.box.x0 + wstate->o.x - margin_l) * scale_x - offset;

	if(text_monospaced == 0)
	{
		/*   get_line_padding(p1,line,justification,&offset,&padding); */
		regs.r[0] = text_font;
		regs.r[1] = (int)p1;
		regs.r[2] = 0x20180;  /* bits 17, 8,7 */
		regs.r[3] = width;
		regs.r[4] = height * scale_y;
		regs.r[7] = t->line_tab[line];

		if(padding > 0)
		{
			coord_block[0] = padding;
			regs.r[5] = (int)coord_block;
			regs.r[2] = 0x201a0;  /* bits 17,8,7,5 */
		}
		os_swi(0x400a1,&regs);   /* Font_ScanString */
		x = (regs.r[3]+offset) / scale_x;

		p = p + ((char *)regs.r[1] - p1);   /* pointer to character at cursor */
	}
	else
	{
		/* system font or monospaced font */
		i = width / text_monospaced_width;
		if(i >= t->line_tab[line])
		{
			i = t->line_tab[line] - 1;
		}
		p = p + i;
		x = (text_monospaced_width * i) / scale_x;
	}

	return(p - t->text_base);
}   /* end of coords_to_index */



void unquote_text(TEXTR *t)
/*************************/
{
	int  ix;
	int  n_chars;

	if(t->region_start >= t->region_end)
	{
		werr(0,"Quote Text: No selected region");
		beep();
		return;
	}

	for(ix=t->region_start; ix<t->region_end; ix++)
	{
		if((ix == t->text_start) || ((t->text_base[ix-1] == '\n') && (t->text_base[ix] != '\n')))
		{
			if(is_quoted_text2(&t->text_base[ix],&n_chars))
			{
				move_up(t,ix+n_chars,n_chars);
			}
		}
	}
	calc_line_tab(t);
	text_reopen(t);
	redraw_text_lines(t,0,-1);

	mark_changed(t);
}   /* unquote_text */




void quote_text(TEXTR *t)
/***********************/
/* Add quote indentation to selected region */
{
	int  ix;
	int  c;
	int  nl_count=0;
	int  quote_len=2;
	char *quote_str = "> ";
	int  end;
	int  offset;

	if(t->region_start >= t->region_end)
	{
		werr(0,"Quote Text: No selected region");
		beep();
		return;
	}

	for(ix=t->region_start; ix<t->region_end; ix++)
	{
		if((ix == t->text_start) || ((t->text_base[ix-1] == '\n') && (t->text_base[ix] != '\n')))
			nl_count++;
	}

	end = t->region_end;
	offset = nl_count * quote_len;

	if(move_down(t,end,offset) != 0)
		return;

	t->region_end = end + offset;

	for(ix=end-1; ix>=t->region_start; ix--)
	{
		t->text_base[ix+offset] = t->text_base[ix];
		if((ix == t->text_start) || (((c = t->text_base[ix-1]) == '\n') && (t->text_base[ix] != '\n')))
		{
			offset -= quote_len;
			memcpy(&t->text_base[ix+offset],quote_str,quote_len);
		}
	}

	calc_line_tab(t);
	text_reopen(t);
	redraw_text_lines(t,0,-1);

	mark_changed(t);
}   /* end of quote_text */



void text_swap_chars(TEXTR *t)
/****************************/
{
	int  ix;
	int  c;
	char *p;

	ix = t->cursor_index - t->cursor_line_start;
	if((ix>0) && (ix < t->line_tab[t->cursor_line]-1))
	{
		p = &t->text_base[t->cursor_index];
		c = p[-1];
		p[-1] = p[0];
		p[0] = c;
		redraw_text_oneline(t,t->cursor_line);
		mark_changed(t);
	}
}   /* end of text_swap_chars */



void text_rot13(TEXTR *t)
/***********************/
/* ROR13 encryption */
{
	int  ix;
	int  c;

	if(t->region_start >= t->region_end)
	{
		werr(0,"ROT13 encryption: No selected region");
		beep();
		return;
	}

	for(ix=t->region_start; ix<t->region_end; ix++)
	{
		if(isalpha(c = t->text_base[ix]))
		{
			if(c <= 'M')
				c += 13;
			else if(c <= 'Z')
				c -= 13;
			else if(c <= 'm')
				c += 13;
			else if(c <= 'z')
				c -= 13;

			t->text_base[ix] = c;
		}
	}


	redraw_text_lines(t,0,-1);

	mark_changed(t);
}   /* end of text_rot13 */




void text_reset_header_order(TEXTR *t)
/************************************/
/* Bit 0:  changing to allow_edit */
{
	int  region_start=0;
	int  region_end=0;

	if(t->allow_edit != 0)
		return;

	if(t->internet_header != t->internet_header2)
	{
		/* if we've re-ordered the internet header, revert to the original version
		   by re-reading the article */

		region_start = t->region_start - t->internet_header;
		region_end = t->region_end - t->internet_header;

		text_read_article(t,t->cardex,t->cardex->addr,t->cardex->text_offset,t->cardex->text_length,1,0);

		if((region_start >= 0) && (region_end > 0))
		{
			t->region_start = region_start + t->internet_header;
			t->region_end = region_end + t->internet_header;
		}
	}

}   /* end of text_reset_header_order */





void text_save_article(TEXTR *t)
/******************************/
{
	int  i;
	dbox d;
	CARD_EXPANDED *cardex;
	char buf[128];

	if(t==NULL)
		return;

	d = t->dbox_card;
	cardex = t->cardex;
	if(cardex==NULL)
		return;

	/* re-read the article if the header has been re-arranged */
	text_reset_header_order(t);

	/* look for "quoted-printable" and replace by "8bit", because we decoded
	   the quoted-printable when viewing the article */

	for(i=0; i<(t->internet_header - 16); i++)
	{
		if(memcmp_lc(&t->text_base[i],"quoted-printable",16)==0)
		{
			memcpy(&t->text_base[i],"8bit",4);
			move_up(t,i+16,12);
			break;
		}
	}


	dbox_getfield(d,CD_AUTHOR,cardex->author,CD_STRLEN);
	dbox_getfield(d,CD_DATE,buf,sizeof(buf));
	cardex->date = encode_date(buf,2) - timezone_offset;  /* back to GMT */

	dbox_getfield(d,CD_TITLE,cardex->title,CD_STRLEN);
	dbox_getfield(d,CD_COMMENT,cardex->comment,CD_STRLEN);
	dbox_getfield(d,CD_SOURCE,buf,sizeof(buf));
	cardex->source = encode_source_name(buf);
	dbox_getfield(d,CD_KEYS,cardex->keywords,CD_STRLEN);
	dbox_getfield(d,CD_CATS,buf,sizeof(buf));
	encode_cats(cardex,buf);

	if(((box_table[cardex->docbox].flags & BOX_LEAVE_UNREAD)==0) && (cardex->status <= STATUS_UNREAD))
		cardex->status = STATUS_READ;

	if(t->attachments)
		cardex->status_other2 |= STATUS_BIT_ATTACH;
	else
		cardex->status_other2 &= ~STATUS_BIT_ATTACH;

	cardfile_update(t->fr,t,cardex,1);

	sprintf(buf,"Size: %d",t->text_length);
	dbox_setfield(d,CD_LENGTH,buf);

	t->changed = 0;
	text_window_title(t);
}   /* end of text_save_article */




void text_remove_tags(TEXTR *t)
/*****************************/
{
	int  ix;
	int  out;
	int  c;
	int  diff;
	int  len;
	int  i;
	int  count;
	int  base;
	int  acc;
	char *p;
	char *p_end;
	char *p_url;
	char *p2;
	int  text_end;
	int  text_start;
	int  html=0;
	MNEM_TAB *tg;
	char buf[128];
	char glyph[8];

	static short unicode_2000[] = {
		0x2010, '-',
		0x2011, '-',
		0x2012, '-',
		0x2013, '-',
		0x2014, '-',
		0x2015, '-',
		0x2016, '',
		0x2017, '_',
		0x2018, '',
		0x2019, '',
		0x201c, '',
		0x201d, '',
		0x201e, '',
		0x2020, '',
		0x2021, '',
		0x2022, '',
		0x2023, '',
		0x2026, '',
		0x2030, '',
		0x2122, '',

		0,0,
	};

	static MNEM_TAB html_glyphs[] = {
		"lt", '<',
		"gt", '>',
		"nbsp", ' ',
		"quot", '"',
		"amp", '&',
		"copy", '',
		"auml", '',
		"ouml", '',
		"uuml", '',
		NULL, 0,
	};

	static MNEM_TAB tags[] = {
		"lt>",3,
		"nl>",1,
		"br>",1,
		"div>",1,
		"tr>",1,
		"li>",2,
		"ul>",2,
		"h1>",2,
		"h2>",2,
		"h3>",2,
		"h4>",2,
		"/h1>",2,
		"/h2>",2,
		"/h3>",2,
		"/h4>",2,
		"p>",2,
		"a href=",4,
		NULL,0
	};

	if((t->attachments > 0) &&
			(t->attach[0].ftype == FILETYPE_HTML) &&
			((t->text_body_end-t->internet_header) < 10))
	{
		/* HTML message with no plain text. Move the attachment into
		   the message */

		text_end = t->attach[0].displ + t->attach[0].length;

		if(t->attach[0].encoding == ENCODE_TEXT_NO_HEADER_Q)
		{
			text_end =  decode_quoted_printable(t,t->attach[0].displ,text_end);
		}

		t->text_body_end = text_end;
		t->attach[0].displ = t->text_body_end;
		t->attach[0].length = 0;
		t->filetype = 0xfff;
		t->cardex->ftype = 0;
		t->region_start = t->region_end = 0;
		html=1;

		/* remove the HTML attachment */
		for(i=1; i<t->attachments; i++)
		{
			memcpy(&t->attach[i-1],&t->attach[i],sizeof(t->attach[i]));
		}
		t->attachments--;
	}
	else
	{
		p2 = &t->text_base[t->text_start];
		p_end = &t->text_base[t->text_body_end-1];
		count = 0; /* look for </ and <....> */

		while(p2 < p_end)
		{
			if(*p2=='<')
			{
				if(p2[1]=='/')
				{
					count |= 1;
					if(count == 3)
					{
						html=1;
						p2 = p_end;
					}
				}
				else if(isalpha(p2[1]))
				{
					i = 0;
					while((p2 <= p_end) && (*p2 != '\n') && (i++ < 8))
					{
						if(*p2++ == '>')
						{
							count |= 2;
							if(count == 3)
							{
								html=1;
								p2 = p_end;  /* exit loops */
							}
						}
					}
				}
			}
			p2++;
		}
	}

	text_start = t->text_start;
	text_end = t->text_body_end;

	if(t->region_start < t->region_end)
	{
		text_start = t->region_start;
		text_end = t->region_end;
	}

	out = text_start;
	p = t->text_base;

	for(ix=text_start; ix<text_end; ix++)
	{
		if(((c = p[ix]) == '\n') && html)
		{
			while((ix < text_end) && isspace(p[ix+1]))  ix++; /* remove SP after NL */
			if(!isspace(p[out-1]))
				c = ' ';
			else
				continue;
		}

		if(html)
		{
			/* supress multiple spaces */
			if(isspace(c))
			{
				c = ' ';
				if(isspace(p[out-1]))
					continue;
			}
		}


		if(c == '&')
		{
			strncpy0(glyph,&p[ix+1],sizeof(glyph));
			if((p2 = strchr(glyph,';')) != NULL)
			{
				*p2 = 0;
				if(isalpha(glyph[0]))
				{
					tg = html_glyphs;
					while(tg->mnem != NULL)
					{
						if(strcmp(glyph,tg->mnem)==0)
						{
							c = tg->value;
							ix += (strlen(glyph)+1);
							break;
						}
						tg++;
					}
				}
				else if(glyph[0]=='#')
				{
					base = 10;
					p2 = glyph;
					if(glyph[0]=='D')
					{
						base = 10;
						p2++;
					}
					else if(tolower(glyph[0])=='x')
					{
						base = 16;
						p2++;
					}

					acc = 0;
					if(base == 10)
					{
						while(isdigit(i = *(++p2)))
						{
							acc *= 10;
							acc += (i-'0');
						}
					}
					else
					{
						while(isxdigit(i = *(++p2)))
						{
							acc *= 16;
							if(isdigit(i))
								acc += (i-'0');
							else
								acc += (tolower(i)-'a'+10);
						}
					}
					if((i == 0) && (acc >= 0x80))
					{
						if(acc < 0x100)
						{
							c = acc;
							ix += (strlen(glyph)+1);
						}
						else
						{
							i = 0;
							while(unicode_2000[i] != 0)
							{
								if(unicode_2000[i] == acc)
								{
									c = unicode_2000[i+1];
									ix += (strlen(glyph)+1);
									break;
								}
								i += 2;
							}
						}
					}
				}
			}
		}
		else if(c == '<')
		{
			p2 = &p[ix+1];
			p_end = strchr(p2,'>');
			if(((p_end-p2) < 150) && ((p_end-p2) > 0))
			{
				c = 0;

				for(tg = tags; tg->mnem != NULL; tg++)
				{
					len = strlen(tg->mnem);
					if(memcmp_lc(p2,tg->mnem,len)==0)
					{
						switch(tg->value)
						{
						case 1:
							/* allow up to 2 NLs */
							if((p[out-1] == '\n') && (p[out-2] == '\n'))
								break;
							c = '\n';
							break;

						case 2:   /* ensure 2 NL's */
							if(p[out-1] != '\n')
							{
								c = '\n';
								p[out++] = c;
							}
							else if(p[out-2] != '\n')
							{
								c = '\n';
							}
							break;

						case 3:
							c = '<';
							break;

						case 4:   /* link */
							p_url = &p[ix+len+2];
							if(*p_url == '#')
								break;   /* local reference */

							p[out++]='<';
							i=0;
							while((i < (sizeof(buf)-2) && (c = buf[i]= *p_url++) != '"') && (c != '>')) i++;
							if(c != '>')
								buf[i++]='>';
							buf[i]=0;

							if(strchr(buf,':')== NULL)
							{
								memcpy(&p[out],"rel://",6);
								out += 6;
							}
							memcpy(&p[out],buf,i);
							out += i;
							c = ' ';
							break;
						}
					}
				}
				ix += (p_end-p2+1);
			}
		}

		if(c != 0)
			p[out++] = c;
	}

	diff = text_end - out;
	move_up(t,text_end,diff);
	calc_line_tab(t);
	redraw_text_lines(t,0,-1);

	mark_changed2(t);
	/*   text_reopen(t); */
	open_article(t,1);
}   /* end of text_remove_tags */



void text_next_paragraph(TEXTR *t, int start)
/*****************************************N*/
{
	while((start < t->text_body_end) && (start < t->sig_start))
	{
		if(isalnum(t->text_base[start]))
		{
			t->cursor_index = start;
			set_caret_pos(t);
			break;
		}
		start++;
	}
}   /* end of text_next_paragraph */




void format_quoted_text2(TEXTR *t)
/********************************/
{
	int  ix;
	char *p;
	char *b;
	int  start;
	int  end;
	int  wrap;
	int  buf_len;
	int  buf_len1;
	int  i;
	int  i_break;
	static int  quote_len;
	int  n_lines=0;
	int  line_start;
	int  ix_break;
	int  prev_ix_break;
	char *b_break;
	int  source_len;
	int  wrap_len;
	char *buf;
	int  quote_blanks0;
	int  quote_blanks1=0;
	int  quote_blanks2=0;
	int  blanks;
	int  text_end;
	char quote_str[80];

	text_end = t->text_body_end;
	if(t->sig_start < text_end)
		text_end = t->sig_start;

	wrap_len = t->wordwrap;

	/* find start of current line */
	ix = t->cursor_index;

	p = t->text_base;
	if(p[ix] == '\n') p--;
	while((ix >= t->text_start) && (p[ix] != '\n'))  ix--;
	ix++;
	if(p[ix] == '\n')
	{
		text_next_paragraph(t,ix);
		return;
	}

	/* find quoting characters */
	quote_len = get_quote_string_length(&p[ix],&quote_blanks2);
	if(quote_len > 13) return;

	memcpy(quote_str,&t->text_base[ix],quote_len);
	quote_str[quote_len] = 0;

	/* look backwards for lines with the same quote string */
	line_start = start = ix;
	quote_blanks0 = 0x7fff;
	while(ix >= t->text_start)
	{
		if(t->text_base[ix] == '\n')
		{
			if((quote_len == 0) && (t->text_base[ix+1] == '\n'))
				break;

			if(quote_len != get_quote_string_length(&t->text_base[ix+1],&blanks))
				break;

			quote_blanks1 = quote_blanks0;
			if(blanks < quote_blanks0)
				quote_blanks0 = blanks;

			if(t->text_base[ix+quote_len+blanks+1] == '\n')
				break;    /* blank line with quote string */

			if(memcmp(&t->text_base[ix+1],quote_str,quote_len) == 0)
			{
				start =  ix+1;
				n_lines++;
			}
			else
				break;
		}
		ix--;
	}

	/* look forwards for lines with the same quote string */
	ix = end = line_start;
	n_lines=0;
	while(++ix < (text_end-1))
	{
		if(t->text_base[ix] == '\n')
		{
			if((quote_len == 0) && (t->text_base[ix+1] == '\n'))
				break;

			if((quote_len == get_quote_string_length(&t->text_base[ix+1],&blanks)) &&
					((ix+blanks+1) < text_end) &&
					(memcmp(&t->text_base[ix+1],quote_str,quote_len) == 0))
			{
				if(t->text_base[ix+quote_len+blanks+1] == '\n')
					break;   /* blank line with quote string */

				if((blanks > quote_blanks2) && (n_lines > 0))
					break;    /* greater indentation, consider this a new paragraph, even without
                            a blank line */

				quote_blanks2 = blanks;
				n_lines++;
			}
			else
				break;
		}
	}
	end = ix;

	if(quote_blanks1 < quote_blanks2)
		quote_blanks2 = quote_blanks1;

	for(blanks=0; blanks<quote_blanks2; blanks++)
		strcat(quote_str," ");
	quote_len += quote_blanks2;

	/* estimate length of re-formatted text, allow an extra quote-string for each line */
	p = t->text_base;   /* in case the heap has shifted */

	wrap = 0;
	i = i_break = 0;
	ix_break = start;
	prev_ix_break = 01;

	for(ix=start; ix<end; ix++)
	{
		if(wrap >= wrap_len)
		{
			if((ix_break==prev_ix_break) || ((ix - ix_break) >= wrap_len))
			{
				ix_break += wrap_len;
				ix = ix_break+2;
				i_break += wrap_len;
			}
			else
			{
				ix = ix_break+1;
			}
			prev_ix_break = ix_break;
			i = i_break;
			i += (quote_len + 2);     /* NL plus quote_str (plus a bit extra) */

			wrap = quote_len;
		}

		if(p[ix] == '\n')
		{
			ix += (quote_len + 1);  /* skip the quote string in the original */
			i++;
			wrap++;
		}
		if(p[ix] <= ' ')
		{
			ix_break = ix;
			i_break = i;
		}

		i++;
		wrap++;
	}
	buf_len1 = i + 64;

	buf = malloc(buf_len1);   /* add a safety margin */
	if(buf == NULL)
		return;
	b = buf;


	p = t->text_base;   /* in case the heap has shifted */

	wrap = 0;
	b_break = b;
	ix_break = start;
	prev_ix_break = -1;

	for(ix=start; ix<end; ix++)
	{
		if(wrap >= wrap_len)
		{
			if(ix_break==prev_ix_break)
			{
#ifdef deleted
				ix_break += wrap_len;
				/*       ix = ix_break+2; */
				b_break += wrap_len;
#endif
			}
			else if((ix - ix_break) >= wrap_len)
			{
#ifdef deleted
				ix_break += wrap_len;
				b_break += wrap_len;
#endif
			}
			else
			{
				ix = ix_break+1;
				/*   } */
				b = b_break;

				prev_ix_break = ix_break;
				i = b-buf;
				*b++ = '\n';
				strcpy(b,quote_str);
				b += quote_len;

				wrap = quote_len;  /* ???? */
			}
		}

		if(p[ix] == '\n')
		{
			/* skip trailing spaces at end of line */
			while((b > buf) && (b[-1] == ' ')) b--;

			b_break = b;
			ix_break = ix + quote_len;

			ix += (quote_len + 1);  /* skip the quote string in the original */

			/* skip any further spaces at start of the line (added 13.4.99) */
			while((ix < end) && (p[ix]==' ')) ix++;

			*b++ = ' ';
			wrap++;
		}
		if(p[ix] <= ' ')
		{
			if(wrap > (quote_len + 1))
			{
				ix_break = ix;
				b_break = b;
			}
		}

		*b++ = p[ix];
		wrap++;
	}
	buf_len = b - buf;
	if(buf_len >= buf_len1)
	{
		werr(0,"Buffer overflow in format-text");
	}

	source_len = end - start;
	if((buf_len > source_len) > 0)
	{
		if(move_down(t,end,buf_len - source_len) == 0)
			memcpy(&t->text_base[start],buf,buf_len);
	}
	else
	{
		move_up(t,end,source_len - buf_len);
		memcpy(&t->text_base[start],buf,buf_len);
	}


	free(buf);

	calc_line_tab(t);
	text_reopen(t);
	redraw_text_lines(t,0,-1);

	if(t->text_type > X_VIEW2)
		mark_changed(t);   /* not for the article viewer */
	else
		mark_changed2(t);    /* allow save but don't warn on discard */

	/* move cursor to start of next paragraph */
	text_next_paragraph(t,end + buf_len - source_len + 1);
}   /* end of format_quoted_text2 */



void format_quoted_text(TEXTR *t)
/*******************************/
{
	int  ix;
	int  save_cursor;

	if(t->wordwrap == 255)
	{
		/* set very long when forwarding a message. Now change it to the
		   usual value */
		if(t->text_type <= X_VIEW2)
			t->wordwrap = options.viewer_wordwrap;
		else
			t->wordwrap = options.reply_wordwrap;
	}

	if((t->region_start < t->region_end) && (t->cursor_index >= t->region_start)
			&& (t->cursor_index <= t->region_end))
	{
		save_cursor = t->cursor_index;  /* remember original cursor position */

		ix = -1;
		t->cursor_index = t->region_start;
		while((t->cursor_index > ix) && (t->cursor_index < t->region_end))
		{
			ix = t->cursor_index;   /* only continue if cursor_index advances */
			format_quoted_text2(t);
		}

		t->cursor_index = save_cursor;
		set_caret_pos(t);
		clear_text_region(t);
	}
	else
	{
		format_quoted_text2(t);
	}
}   /* end of format_quoted_text */





void text_format_text(TEXTR *t)
/*****************************/
{
	if(dbox_persist())
	{
		t->region_start = t->text_start;
		t->region_end = t->text_body_end-1;
		format_quoted_text(t);
		clear_text_region(t);
	}
	else
	{
		format_quoted_text(t);
	}
}   /* end of text_format_text */





void claim_entity_request(wimp_eventdata *d)
/******************************************/
{
	/* check against own task handle */
	if((int)d->msg.hdr.task == (int)wimpt_task())
		return;

	if(d->msg.data.words[0] & 4)
	{
		/* claiming the global clipboard */
		if(clipboard != NULL)
		{
			flex_free((flex_ptr)&clipboard);
			clipboard = NULL;
			clipboard_size = 0;
		}
	}
}   /* end of claim_entity_request */



void clipboard_request(wimp_eventdata *d)
/***************************************/
{
	wimp_msgstr m;

	/* another application requesting data from our clipboard */
	if(clipboard == NULL)
		return;

	m.hdr.size = 60;
	m.hdr.your_ref = d->msg.hdr.my_ref;
	m.hdr.action = wimp_MDATASAVE;
	memcpy(&m.data.words[0],&d->msg.data.words[0],16);
	m.data.words[4] = clipboard_size;
	m.data.words[5] = 0xfff;
	strcpy((char *)&m.data.words[6],"Pluto");

	wimp_sendmessage(wimp_ESEND,&m,d->msg.hdr.task);
}   /* end of clipboard_request */



int clipboard_send(wimp_eventdata *d)
/************************************/
/* We've got a datasaveok message, now save the clipboard
   data to file, and send a dataload */
{
	wimp_msgstr m;

	FILE *f;

	if(clipboard == NULL)
		return(FALSE);


	f = fopen(d->msg.data.datasaveok.name,"w");
	if(f==NULL)
	{
		/* can't write to this file, try wimpscrap */

		strcpy(d->msg.data.datasaveok.name,"<wimp$scrap>");
		f = fopen(d->msg.data.datasaveok.name,"w");
		if(f==NULL)
		{
			return(FALSE);
		}
	}

	fwrite_werr(clipboard,clipboard_size,1,f);
	fclose(f);

	m.hdr.size = d->msg.hdr.size;
	m.hdr.your_ref = d->msg.hdr.my_ref;
	m.hdr.action = wimp_MDATALOAD;
	memcpy(&m.data.words[0],&d->msg.data.words[0],16);
	m.data.words[4] = clipboard_size;
	m.data.words[5] = 0xfff;
	strcpy((char *)&m.data.words[6],d->msg.data.datasaveok.name);

	wimp_sendmessage(wimp_ESEND,&m,d->msg.hdr.task);
	return(TRUE);
}   /* end of clipboard_send */



int dragdrop_claiming = 0;
int dragdrop_pointer_altered = 0;


int is_text_window(wimp_w w)
/**************************/
{
	return(TRUE);
}   /* end of is_text_window */




void dragdrop_start(wimp_mousestr *m,int type, void *handle, wimp_w window)
/*************************************************************************/
{
	wimp_dragstr dragstr;
	os_regset drag;

	/* from xfersend.c   handler wasn't being removed during a Save-Drag if
	   the application (eg. Posty) doesn't send DataSaveAck after an ignored
	   RamFetch */
	win_remove_unknown_event_processor(xfersend__unknowns, 0);

	dragstr.box.x0 = m->x;
	dragstr.box.x1 = m->x;
	dragstr.box.y0 = m->y;
	dragstr.box.y1 = m->y;

	drag.r[0]=0x45;
	drag.r[1]=1;             /* wimp sprite area */
	drag.r[2]=(int)"file_fff";
	drag.r[3]=(int)&dragstr.box;
	os_swi(0x42400,&drag);   /* DragASprite_Start */

	drag_last_line = -1;
	drag_last_x = -1;
	drag_handle = handle;
	drag_window_type = type;
	drag_window = window;

	null_events(2,1);     /* turn on for drag */
}   /* end of dragdrop_start */




void dragdrop_continue(TEXTR *t, int open, wimp_wstate *wstate, wimp_wstate *wstate1, wimp_mousestr *m)
/*****************************************************************************************************/
{
}   /* dragdrop_continue */



void dragdrop_complete(wimp_mousestr *m)
/**************************************/
{
}   /* end of dragdrop_complete */



void dragdrop_request(wimp_eventdata *d)
/**************************************/
{
	int  flags;
	wimp_w window;
	wimp_msgstr m;

	window = (wimp_w)d->msg.data.words[0];
	flags = d->msg.data.words[4];

	if(dragdrop_claiming)
	{
		if(((flags & 0x10)==0) && (is_text_window(window)))
		{
			/* we are in a text window */
			m.hdr.size = 28;
			m.hdr.your_ref = d->msg.hdr.my_ref;
			m.hdr.action = 18;   /* Message_DragClaim*/
			m.data.words[0] = 0;
			m.data.words[1] = -1;
			wimp_sendmessage(wimp_ESEND,&m,d->msg.hdr.task);

			/* draw ghost caret */
		}
		else
		{
			dragdrop_claiming = 0;
			/* undraw ghost caret */
		}
	}
	else
	{
		if(((flags & 0x10)==0) && (is_text_window(window)))
		{
			/* we are in a text window */
			m.hdr.size = 28;
			m.hdr.your_ref = d->msg.hdr.my_ref;
			m.hdr.action = 18;   /* Message_DragClaim*/
			m.data.words[0] = 0;
			m.data.words[1] = -1;
			wimp_sendmessage(wimp_ESEND,&m,d->msg.hdr.task);

			/* draw ghost caret */
		}
	}
}   /* end of dragdrop_request */


void dragdrop_claim(wimp_eventdata *d)
/************************************/
{
}   /* end of dragdrop_claim */





void text_region_delete(TEXTR *t, int snip)
/*****************************************/
{
	char *p;
	char *p2;
	char *p_line;
	int  region_size;
	int  replace_size=0;
	int  increase;
	int  quote_str_len;
	char *snip_string = "\n[snip]\n";
	char *rs;
	char replace_string[80];

	region_size = t->region_end - t->region_start;

	if(snip)
	{
		p = msgs_lookup("Ysnip");
		if(p != NULL)
		{
			snip_string = p;
			while(*p != 0)
			{
				if(*p == '|')
					*p = '\n';
				p++;
			}
		}

		p = &t->text_base[t->region_start];
		p2 = p + region_size;

		rs = replace_string;
		if((t->region_start > 0) && (p[-1] != '\n'))
			*rs++ = '\n';
		else if((t->region_start > 1) && (p[-2] == '\n'))
		{
			if(snip_string[0] == '\n')
				snip_string++;  /* don't add a NL if preceded by a blank line */
		}

		strcpy(rs,snip_string);
		rs += strlen(snip_string);

		if(*p2 != '\n')
		{
			*rs++ = '\n';

			if(p2[-1] != '\n')
			{
				/* snip in the midde of a line.  Find the start of the line to see
				   if there are any quote characters to be applied */

				quote_str_len = 0;
				for(p_line = p2; (p_line > &t->text_base[0]) && (p_line[-1] != '\n'); p_line--)
				{
				}
				if(is_quoted_text(p_line))
				{
					quote_str_len = get_quote_string_length(p_line,NULL);
				}
				memcpy(rs,p_line,quote_str_len);
				rs+=quote_str_len;
			}
		}
		else
		{
			if((p2[1] == '\n') && (rs[-1] == '\n'))
				rs--;       /* don't add a NL if a blank line follows */
		}

		*rs = 0;
		replace_size = strlen(replace_string);
	}

	if(t->region_end == 0)
		return;    /* no marked region */

	if(t->cursor_index > t->region_start)
	{
		if(t->cursor_index <= t->region_end)
		{
			/* cursor was in the deleted region, re-position it */
			t->cursor_index = t->region_start;
		}
		else
		{
			t->cursor_index -= region_size;
		}
	}

	increase = replace_size - region_size;
	if(increase < 0)
		move_up(t,t->region_end, -increase);
	else if(increase > 0)
		move_down(t,t->region_end, increase);

	if(replace_size > 0)
		memcpy(&t->text_base[t->region_start],replace_string,replace_size);

	if(increase < 0)
		text_recalc_delete(t,t->region_start,increase);
	else
		recalc_line_tab(t,t->cursor_line,increase);

	set_caret_pos(t);

	/* remove region */
	t->region_start = t->region_end = 0;

	redraw_text_lines(t,0,-1);
	mark_changed(t);
}   /* end of text_region_delete */




void text_character_delete(TEXTR *t)
/*******************************/
{
	if((options.edit_style != EDIT_EDIT) && (t->region_start < t->region_end))
	{
		if(options.edit_style == EDIT_STANDARD)
		{
			beep();
			if(query("Delete marked text ?","DELETE") == 0)
				return;
		}

		text_region_delete(t,0);
		mark_changed(t);
		return;
	}

	if(t->cursor_index <= 0)
		return;

	if((t->show_message_headers == 0) && (t->cursor_index <= t->internet_header))
		return;

	move_up(t,t->cursor_index--,1);

	if(t->cursor_index < t->cursor_line_start)
	{
		text_recalc_delete(t,t->cursor_index,-1);
	}
	else
		text_recalc_delete(t,t->cursor_index,-1);

	set_caret_pos(t);


	mark_changed(t);
}   /* end of text_character_delete */







void retrieve_news_article(char *message_id, int local, CARD *cptr_ref)
/*********************************************************************/
{
	char *p;
	int  c;
	CARD *cptr;
	char url[180];
	char message_id2[200];

	char *deja_query;
	char *deja_msgid;
#ifdef deleted
	static char *deja_msgid = "http://www.deja.com/=dnc/msgid.xp?MID=%%3C%s%%3E&ST=&AH=1";
	static char *deja_query = "http://www.deja.com/=dnc/dnquery.xp?QRY=%s";
#endif

	if((message_id[0]=='<') && ((p = strrchr(&message_id[1],'>'))!=NULL))
	{
		/* remove < > around the message id */
		*p = 0;
		message_id++;
	}

	if(local)
	{
		/* first check Pluto's article store and display in an additional article
		   viewer if found */
		sprintf(url,"<%s>",message_id);

		visdelay2_begin();
		cptr = msgid_lookup(gethash32(url),cptr_ref,0);
		visdelay2_end();

		if(cptr != NULL)
		{
			article_view2(NULL,-1,cptr);
			return;
		}
	}

	/* if not found, look it up in deja-news */
	p = message_id2;
	while((c = *message_id++) != 0)
	{
		if(strchr("$+<>",c) != NULL)
		{
			sprintf(p,"%%%.2X",c);
			p+=3;
		}
		else
		{
			*p++ = c;
		}
	}
	*p = 0;

	if(strchr(message_id2,'@')==NULL)
	{
		deja_query = msgs_lookup("Yquery");
		sprintf(url,deja_query,message_id2);
	}
	else
	{
		deja_msgid = msgs_lookup("Ymsgid");
		sprintf(url,deja_msgid,message_id2);
	}
	pass_url_to_browser(url);
}   /* end of retrieve_news_article */





static int text_extract_url(TEXTR *t, int  ix, char *word, int *length)
/*********************************************************************/
{
	char *p;
	int  i;
	int  c;
	int  in_brackets=0;
	int  word_start;
	int  terminator;

	p = &t->text_base[ix];

	while((p > t->text_base) && (p[-1] > ' ') && (strchr("(<[{)>]}\"'",p[-1]) == NULL))
		p--;   /* find start of word */

	if(p[-1] == '<')
		in_brackets = 1;

	word_start = p - t->text_base;

	/* ignore URL: at start */
	if(memcmp_lc(p,"url:",4)==0)
		p+=4;

	i=0;
	while(((c = word[i] = p[i]) >= ' ') && (strchr(")>]\"",c)==NULL) && (i < 399))
	{
		if((in_brackets == 0) && (c == ' '))
			break;

		i++;
	}
	terminator = word[i];
	word[i]=0;

	while((i > 2) && (strchr(".,:;-?!\"'",word[i-1]) != NULL))
	{
		if((terminator=='>') && (strchr(".-!",word[i-1]) != NULL))
			break;  /* allow URL ending in . etc if terminator is > */

		word[i-1] = 0;   /* delete trailing punctuation */
		i--;
	}

	if(length != NULL)
		*length = i;
	return(word_start);
}   /* end of text_extract_url */





int text_is_url(TEXTR *t, int ix, char *word, int *start)
/*******************************************************/
{
	int  i;
	int  c;
	int  dots;
	int  after_dots;
	int  slashes;
	int  at_sign;
	int  in_brackets = 0;
	int  digits;
	int  alphas;
	int  word_start;

	/* extract the word into buffer */
	word_start = text_extract_url(t,ix,word,NULL);
	if(start != NULL)
		*start = word_start;

	i=0;
	dots = 0;
	after_dots=0;
	slashes=0;
	at_sign=0;
	digits=0;
	alphas=0;

	for(i=0;; i++)
	{
		c = word[i];
		if(c == 0)
			break;

		if(c == '@')
			at_sign++;
		if(c == '.')
			dots++;
		if(isalnum(c) && (dots>=1))
			after_dots++;
		if(isdigit(c) && (at_sign==0))
			digits++;
		if(isalpha(c))
			alphas++;
		if(memcmp(&word[i],"://",3)==0)
		{
			slashes=1;
			dots++;
		}
	}

	if(t->text_base[word_start-1] == '<')
		in_brackets = 1;

	if(memcmp(word,"file:",5)==0)
		return(5);

	if(memcmp(word,"news:",5)==0)
		return(4);

	if((in_brackets) && (at_sign == 1) && (digits >= 2))
		return(2);   /* message-id or perhaps an email address */

	if((at_sign >= 1) && (slashes==0))
	{
		if(after_dots >= 1)
			return(1);   /* an email address */
	}

	if((memcmp(word,"rel://",6)==0) && (t->text_type <= X_VIEW2))
	{
		/* a relative address */
		return(6);
	}

	if(isalnum(word[0]) && isalnum(word[1]) && (dots > 1) && (after_dots>1))
	{
		return(3);   /* could be a web address */
	}
	return(0);
}   /* end of text_is_url */





void text_menu_url(TEXTR *t, char *hit)
/*************************************/
{
	char *p;
	CARD *cptr;
	int  field = 0;
	int  ix;
	TEXTR *t_write;
	int  url_start;
	int  url_length;
	char word[400];
	char word2[131];

	url_start = text_extract_url(t,text_menu_index,word,&url_length);
	p = word;

	switch(hit[0])
	{
	case 1:   /* send email */
		new_reply(NULL,word,NULL,NULL,0,NULL);
		break;

	case 2:   /* add to CC */
		t_write = reply_data_find(0x11);
		if(t_write)
		{
			if(t_write->text_type == X_MAIL)
				field = 10;
			else
				field = 11;
		}
		break;

	case 3:   /* add to BCC */
		t_write = reply_data_find(0x13);
		if(t_write)
		{
			field = 11;
		}
		break;

	case 4:   /* add to address book */
		if(memcmp(word,"mailto:",7)==0)
			p+=7;
		addrlist_add_email(p,0);
		break;

	case 5:   /* add to Write news/mail window */
		t_write = reply_data_find(0x11);
		if(t_write)
		{
			url_length = strlen(word);
			ix = t_write->cursor_index;
			if(move_down(t_write,ix,url_length+1) == 0)
			{
				memcpy(&t_write->text_base[ix],word,url_length);
				t_write->text_base[ix+url_length] = '\n';
			}
			calc_line_tab(t_write);
			redraw_text_lines(t_write,0,-1);
			mark_changed(t_write);

			t_write->cursor_index += (url_length+1);
			set_caret_pos(t_write);
		}
		break;

	case 6:   /* fetch url */
		pass_url_to_browser(word);
		break;

	case 7:   /* open message-id */
		word[128] = 0;
		p = word;
		if(memcmp(word,"news:",5)==0)
			p = &word[5];
		sprintf(word2,"<%s>",p);

		visdelay2_begin();
		cptr = msgid_lookup(gethash32(word2),t->cptr,0);
		visdelay2_end();

		if(cptr == NULL)
			werr(0,"Message ID not found in Pluto");
		else
		{
			article_view2(NULL,-1,cptr);
		}
		break;

	case 8:   /* read news from DejaNews */
		p = word;
		if(memcmp(word,"news:",5)==0)
			p = &word[5];
		retrieve_news_article(p,0,NULL);
		break;

	case 9:   /* save as */
		save_url(t,word);
		break;
	}

	if(field > 0)
	{
		reply_add_to_field(t_write->dbox_card,field,word);
		if(t_write->text_type == X_NEWS)
			t_write->button_bar_height = NEWS_BUTTON_BAR_HEIGHT2;
		else
			t_write->button_bar_height = MAIL_BUTTON_BAR_HEIGHT2;
		open_article(t_write,2);
	}
}   /* end of text_menu_url */





void double_click_text(TEXTR *t)
/******************************/
{
	int  i;
	char *p;
	int  word_start;
	int  url_type;
	char word[400];
	char word2[400+7];
	wimp_msgstr msg;

	/* extract the word */
	url_type = text_is_url(t,t->cursor_index,word,&word_start);

	switch(url_type)
	{
	case 0:   /* not a URL */
		/* highlight the word */
		for(i=word_start; i<t->text_body_end; i++)
		{
			if(t->text_base[i] <= ' ')
				break;
		}
		if(i > word_start)
		{
			clear_text_region(t);
			t->region_start = word_start;
			t->region_end = i;
			redraw_text_oneline(t,t->cursor_line);
		}

		spell_set_word(t,t->cursor_index,0);
		break;

	case 1:   /* email address */
		new_reply(NULL,word,NULL,NULL,0,NULL);
		break;

	case 2:   /* probably message id (or perhaps email address) */
		retrieve_news_article(word,1,t->cptr);
		break;

	case 3:   /* web address */
		pass_url_to_browser(word);
		break;

	case 4:   /* news: */
		retrieve_news_article(&word[5],1,t->cptr);
		break;

	case 5:   /* file: */
		p = &word[5];
		while(*p == '/') p++;
		strcpy(msg.data.dataopen.name,p);

		msg.data.dataopen.w = t->w_text;
		msg.data.dataopen.i = -1;
		msg.data.dataopen.x = 0;
		msg.data.dataopen.y = 0;
		msg.data.dataopen.size = get_filelength(p);
		if(akbd_pollsh())
			msg.data.dataopen.type = 0xfff;  /* adjust-click, open as Text */
		else
			msg.data.dataopen.type = get_filetype(p);

		msg.hdr.size = (strlen(msg.data.dataopen.name) + 44 + 4) & ~3;
		msg.hdr.your_ref = 0;
		msg.hdr.action = wimp_MDATAOPEN;

		wimp_sendmessage(wimp_ESENDWANTACK,&msg,0);
		break;

	case 6:   /* rel: */
		/* a relative address */
		if(memcmp(t->cardex->author,"http://",7)==0)
		{
			/* delete any non-directory name at the end */
			strcpy(word2,t->cardex->author);
			p = strrchr(word2,'/');
			sprintf(p+1,"%s",&word[6]);
			strcpy(word,word2);
		}
		break;
	}
}   /* end of double_click_text */






void drag_start(wimp_mousestr *m,int type, void *handle, wimp_w window)
/**************************************************************/
{
	wimp_dragstr dragstr;
	wimp_wstate wstate;
	TEXTR *t;

	t = (TEXTR *)handle;

	/* from xfersend.c   handler wasn't being removed during a Save-Drag if
	   the application (eg. Posty) doesn't send DataSaveAck after an ignored
	   RamFetch */
	win_remove_unknown_event_processor(xfersend__unknowns, 0);

	dragstr.window = m->w;
	dragstr.type = 7;
	dragstr.box.x0 = m->x;
	dragstr.box.x1 = m->x;
	dragstr.box.y0 = m->y;
	dragstr.box.y1 = m->y;

	drag_last_line = -1;
	drag_last_x = -1;
	drag_handle = handle;
	drag_window_type = type;
	drag_window = window;

	wimp_get_wind_state(drag_window,&wstate);
	if((type==1) && (t->region_end)==0)
	{
		/* no region to clear, don't need to redraw the whole window */
		coords_to_index(t,&wstate,m->x,m->y,&drag_last_line,NULL);
	}

	memcpy(&dragstr.parent,&wstate.o.box,sizeof(wimp_box));  /* restrict to window */
	wimp_drag_box(&dragstr);
	null_events(2,1);     /* turn on for drag */
}   /* end of drag_start */




static int text_click_to_index(TEXTR *t, wimp_mousestr *mouse, int *xx, int *yy)
/***********************************************************************/
/* Find the index into the text for the current mouse position */
{
	int  screen_y;
	int  line;                  /* line within the text */
	int  i;
	int  x;
	int  y;
	int  index;
	int  height;
	int  width;
	char *p;
	char *p1;
	int  padding=0;
	int  offset=0;
	wimp_wstate wstate;
	os_regset regs;

	static int coord_block[9] = {0,0,0,0,-1,0,0,0,0};

	screen_y = mouse->y;      /* screen coord in click */

	wimp_get_wind_state(t->w_text,&wstate);
	i = wstate.o.box.y1 - screen_y;   /* displacement down the window */

	line = warea_to_line(t,wstate.o.y - i,&y,&index,text_line_height);
	height = text_line_height;
	p = &t->text_base[index];
	p1 = de_tab_text(p, t->line_tab[line]);

	t->cursor_line = line;
	t->cursor_line_start = index;

	if(t->cursor_line < 0)
		t->cursor_line = 0;
	if(t->cursor_line_start < 0)
		t->cursor_line_start = 0;

	/* find character at cursor, and its x position */
	width = (mouse->x - wstate.o.box.x0 + wstate.o.x - margin_l) * scale_x - offset;

	if(text_monospaced == 0)
	{
		/*   get_line_padding(p1,line,justification,&offset,&padding); */
		regs.r[0] = text_font;
		regs.r[1] = (int)p1;
		regs.r[2] = 0x20180;  /* bits 17, 8,7 */
		regs.r[3] = width;
		regs.r[4] = height * scale_y;
		regs.r[7] = t->line_tab[line];

		if(padding > 0)
		{
			coord_block[0] = padding;
			regs.r[5] = (int)coord_block;
			regs.r[2] = 0x201a0;  /* bits 17,8,7,5 */
		}
		os_swi(0x400a1,&regs);   /* Font_ScanString */
		x = (regs.r[3]+offset) / scale_x;

		p = p + ((char *)regs.r[1] - p1);   /* pointer to character at cursor */
	}
	else
	{
		/* system font or monospaced font */
		i = (width + (text_monospaced_width/2))/ text_monospaced_width;
		if(i >= t->line_tab[line])
		{
			i = t->line_tab[line] - 1;  /* max. characters on a this line */
		}
		p = p + i;
		x = (text_monospaced_width * i) / scale_x;
	}

	if(xx != NULL)
		*xx = x;
	if(yy != NULL)
		*yy = y;
	return(p - t->text_base);
}   /* end of text_click_to_index */




void click_text_window(TEXTR *t, wimp_eventdata *d)
/**************************************************/
{
	int  x;
	int  y;
	int  bbits;
	int  mid_point;
	wimp_caretstr caretstr;

	bbits = d->but.m.bbits;

	t->cursor_index = text_click_to_index(t,&d->but.m,&x,&y);

	if(t->cursor_index >= t->text_body_end)
		t->cursor_index = t->text_body_end - 1;


	if((options.edit_style != EDIT_EDIT) && ((bbits==wimp_BCLICKLEFT) || (bbits==wimp_BDRAGLEFT)))
	{
		if(t->region_end > t->region_start)
		{
			/* selected region is in this window, only clear if we are clicking outside the region */
			if((t->cursor_index < t->region_start) || (t->cursor_index >= t->region_end))
			{
				clear_text_region(t);
			}
		}
		else
		{
			clear_text_region(t);  /* ??? */
		}
	}

	switch(bbits)
	{
	case wimp_BCLICKLEFT:
		t->last_cursor_index = t->cursor_index;
		scroll_as_you_speak = 0;

		null_events(2,0);   /* in case drag has been left on somehow */
		break;

	case wimp_BLEFT:   /* double click */
		if(options.edit_style == EDIT_EDIT)
			clear_text_region(t);
		double_click_text(t);
		break;

	case wimp_BRIGHT:
		if(options.edit_style == EDIT_EDIT)
			clear_text_region(t);
		break;

	case wimp_BCLICKRIGHT:
	case wimp_BDRAGRIGHT:
		mid_point = t->region_end - t->region_start;
		if(mid_point > 0)
		{
			/* there is already a selection */
			mid_point = t->region_start + mid_point/2;
			if(t->cursor_index > mid_point)
			{
				t->region_end = t->cursor_index;
				drag_point = t->region_start;
			}
			else
			{
				t->region_start = t->cursor_index;
				drag_point = t->region_end;
			}
		}
		else
		{
			if(t->cursor_index >= t->last_cursor_index)
			{
				t->region_start = t->last_cursor_index;
				t->region_end = t->cursor_index;
			}
			else
			{
				t->region_start = t->cursor_index;
				t->region_end = t->last_cursor_index;
			}
		}

		if((options.edit_style == EDIT_EDIT) && (t_region != t))
			clear_text_region(t_region);

		t_region = t;
		redraw_text_lines(t,0,-1);

		if(bbits==wimp_BDRAGRIGHT)
		{
			if(mid_point == 0)
				drag_point = t->cursor_index;  /* no region currently set */

			drag_start(&d->but.m,1,(void *)t,t->w_text);
		}
		break;

	case wimp_BDRAGLEFT:
		drag_point = t->cursor_index;

		if((t->cursor_index > t->region_start) && (t->cursor_index < t->region_end))
		{
#ifdef deleted
			dragdrop_start(&d->but.m,3,(void *)t,t->w_text);
#else
			clear_text_region(t);
			t->region_start = t->region_end = t->cursor_index;
			drag_start(&d->but.m,1,(void *)t,t->w_text);
#endif
		}
		else
		{
			if(options.edit_style == EDIT_EDIT)
				clear_text_region(t);

			t->region_start = t->region_end = t->cursor_index;
			drag_start(&d->but.m,1,(void *)t,t->w_text);
		}
		break;
	}


	caretstr.w = t->w_text;
	caretstr.i = -1;
	caretstr.x = x + margin_l;
	caretstr.y = y;
	caretstr.height = text_line_height;
	caretstr.index = 0;
	wimp_set_caret_pos(&caretstr);

	/*   set_scroll_as_you_speak(t,0); */
}   /* end of click_text_window */






void text_drag_continue(TEXTR *t, int open, wimp_wstate *wstate, wimp_wstate *wstate1, wimp_mousestr *m)
/******************************************************************************************************/
{
	int  index;
	int  line;
//   int  mid_point;
	int  this_x;
	static int last_index = -1;

	if(t == NULL)
		return;

	this_x = m->x - wstate->o.box.x0;
	index = coords_to_index(t,wstate1,m->x,m->y,&line,NULL);

	if(index == last_index)
		return;

	if((wstate->o.box.y1 - wstate->o.box.y0 - wstate->o.y) > (t->n_lines * text_line_height))
	{
		wstate->o.y += 40;   /* don't scroll past end of text */
	}

	/* any scrolling to be done ? */
	if(open)
	{
		open_article_window(t,&wstate->o,0);
	}

	if((options.edit_style == EDIT_EDIT) && (t_region != t))
		clear_text_region(t_region);
	t_region = t;

#ifdef deleted
	mid_point = t->region_end - t->region_start;
	mid_point = t->region_start + mid_point/2;
	if(index > mid_point)
	{
		drag_side = 2;
		t->region_end = index;
	}
	else
	{
		drag_side = 1;
		t->region_start = index;
	}
#endif


	if(index > drag_point)
	{
		t->region_start = drag_point;
		t->region_end = index;
	}
	else if(index < drag_point)
	{
		t->region_start = index;
		t->region_end = drag_point;
	}
	else
	{
		t->region_start = 0;
		t->region_end = 0;
	}

	if(drag_last_line == -2)
		drag_last_line = line;   /* starting, but only need to redraw this line */

	if(drag_last_line == -1)
		redraw_text_lines(t,0,-1);  /* 1st time, to clear previous region */
	else
	{
		if((drag_last_line == line) && (drag_last_x >= 0))
		{
			redraw_text_partline(t,line,drag_last_x,this_x);
		}
		else
		{
			redraw_text_lines(t,drag_last_line,line);
		}
	}

	drag_last_line = line;
	last_index = index;
	drag_last_x = this_x;
}   /* end of text_drag_continue */





void drag_continue()
/****************/
{
	int  open = 0;
	int  y_scroll;
	int  i;
	wimp_mousestr m;
	wimp_wstate wstate;
	wimp_wstate wstate1;


	wimp_get_point_info(&m);
	wimp_get_wind_state(m.w,&wstate);
	memcpy(&wstate1,&wstate,sizeof(wstate));

	/* scroll if we are near the edge of the window */

	if(text_line_height > 40)
		y_scroll = text_line_height;
	else
		y_scroll = 40;

	if((i = (m.y - wstate.o.box.y0)) < 40)
	{
		wstate.o.y -= y_scroll;
		open = 1;

		if(i < 4)
			wstate.o.y -= y_scroll;   /* scroll faster */
	}
	else if((i = (wstate.o.box.y1 - m.y)) < 40)
	{
		wstate.o.y += (y_scroll + text_font_offset);
		open = 1;

		if(i < 4)
			wstate.o.y += y_scroll;
	}

	if((m.x - wstate.o.box.x0) < 40)
	{
		wstate.o.x -= 40;
		open = 1;
	}
	else if((wstate.o.box.x1 - m.x) < 40)
	{
		wstate.o.x += 40;
		open = 1;
	}

	if(drag_window != m.w)
		return;

	switch(drag_window_type)
	{
	case 1:
		text_drag_continue((TEXTR *)drag_handle,open,&wstate,&wstate1,&m);
		break;

	case 2:
		list_drag_continue((FOLDREC *)drag_handle,open,&wstate,&wstate1,&m);
		break;

	case 3:    /* dragdrop from text */
		dragdrop_continue((TEXTR *)drag_handle,open,&wstate,&wstate1,&m);
		break;

	case 4:
		blist_drag_continue((BLIST *)drag_handle,open,&wstate,&wstate1,&m);
		break;
	}
}   /* end of drag_continue */




void drag_complete()
/******************/
{
	wimp_mousestr mousestr;

	wimp_get_point_info(&mousestr);

	switch(drag_window_type)
	{
	case 3:
		dragdrop_complete(&mousestr);
		break;
	}
}   /* drag_complete */




void cursor_prev_word(TEXTR *t)
/*****************************/
{
	if(t->text_base[t->cursor_index] > ' ')
		t->cursor_index--;

	if(t->text_base[t->cursor_index] <= ' ')
	{
		/* find end of previous word */
		while((t->cursor_index >= 0) && (t->text_base[t->cursor_index] <= ' '))
			t->cursor_index--;
	}

	/* find start of previous word */
	while((t->cursor_index >= 0) && (t->text_base[t->cursor_index] > ' '))
		t->cursor_index--;

	t->cursor_index++;
}   /* end of cursor_prev_word */




void cursor_next_word(TEXTR *t)
/*****************************/
{
	/* find end of current word */
	if(t->text_base[t->cursor_index] > ' ')
	{
		while((t->cursor_index < t->text_body_end) && (t->text_base[t->cursor_index] > ' '))
			t->cursor_index++;
	}

	/* find start of next word */
	while((t->cursor_index < t->text_body_end) && (t->text_base[t->cursor_index] <= ' '))
		t->cursor_index++;

	if(t->cursor_index >= t->text_body_end)
		t->cursor_index = t->text_body_end-1;

}   /* end of cursor_next_word */




void text_window_close(TEXTR *t)
/******************************/
{
	wimp_caretstr caret;

	wimp_get_caret_pos(&caret);

	if((caret.w == t->w_card) || (caret.w == t->w_text))
	{
		/* this window had the input focus */
		if(t->parent == 0)
			set_focus(text_view->w_text);
		else if(t->parent->ixlist != NULL)
		{
			set_focus(t->parent->window);
		}
	}

	speak_stop(t);
	t->allow_edit = 0;
	t->changed = 0;

	/* free the text area if it is large */
	if(t->text_type == X_VIEW2)
	{
		text_data_free(t);
	}
	else if((t->text_base != NULL) && (t->text_buf_size > 6000))
	{
		flex_free((flex_ptr)&t->text_base);
		t->text_base = NULL;
		t->text_buf_size = 0;
	}
}   /* end of text_window_close */



void text_close_window(TEXTR *t)
/******************************/
{
	if(t->w_text == 0)
		return;
	if(card_check_changed(t)!=0)
		return;

	if(t==textr_spellcheck)
	{
		close_spell_window(t);
	}

	if(t->text_type <= X_VIEW2)
	{
		if((t->new_file) && (t->text_length <= 1) &&
				(t->cardex->author[0]==0) && (t->cardex->title[0]==0))
		{
			/* a new, blank article.  discard it */
			cardfile_remove(t,t->cardex->card_ptr,BOX_BIN);
		}

		card_set_read(t->fr);
		dbox_hide(t->dbox_card);
		t->changed = 0;

		text_window_close(t);
		wimp_close_wind(t->w_text);
		wimp_close_wind(t->w_attach);
	}
	else
	{
		reply_close(t);
	}
}   /* end of text_close_window */







static void text_selected_box1(int box, int copy)
/***********************************************/
{
	CARD *cptr;
	CARD *cptr_new;
	CARD_EXPANDED *cardex;
	int  x1, x2;
	int  box1;
	int  external=0;

	if(box_table[box].name[0]==0)
	{
		werr(0,"Box %d does not exist",box);
		return;
	}

	if(check_lock_lists(0xffff) != 0)
		return;

	if(box_password_check(box,1) == 0)
		return;

	cardex = current_t->cardex;

	if(cardex->status <= STATUS_UNREAD)
		cardex->status = STATUS_READ;   /* set to read */

	card_set_read(current_t->fr);

	if((box1 = cardex->docbox) == box)
		return;    /* already in this box */

	if((box_to_cf(box) > 0) || (box_to_cf(box1) > 0))
		external=1;

	x1 = boxlist_adjust_count(box,1,cardex->status);
	x2 = 0;

	if(copy || external)
	{
		/* copy into box */
		cptr = cardex->card_ptr;
		cardex->docbox = box;
		cardex->copy_box = 0;
		cardex->text_anchor = &current_t->text_base;
		cardex->text_start = 0;
		cardex->text_length = current_t->text_length;
		cardex->status = cptr->status & STATUS_MASK;
		cardex->replied = (cptr->status >> 3) & 1;
		article_open_shortest(box,0,-1);
		cptr_new = cardfile_add(cardex);
		article_file_close();
		redraw_list_lines(NULL,0);
		cardex->docbox = box1;

		if(external && (copy == 0))
		{
			/* delete original copy */
			cardfile_remove3(NULL,cptr);
			cptr->status = cptr->status & ~STATUS_MASK2 | STATUS_HIDDEN;

			current_t->cptr = cptr_new;
			unpack_card(cptr_new,cardex);
		}
		else
		{
			cardex->card_ptr = cptr;
		}
	}
	else
	{
		if(cardex->docbox == BOX_BIN)
			article_delete(current_t->fr,cardex->card_ptr,1,1);  /* resurrect this article */

		x2 = boxlist_adjust_count(cardex->docbox,-1,cardex->status);
		cardex->docbox = box;

		cardfile_remove(current_t,cardex->card_ptr,box);
	}
	dbox_setfield(current_t->dbox_card,CD_BOXIN,box_table[box].name);

	if(x2 == 2)
		set_boxlist_extent(7);  /* boxes may become hidden/unhidden */
	else if(x1 == 1)
		boxlist_recalc();       /* boxes status may have changed */


}   /* end of text_selected_box1 */



void text_selected_box(int *hits)
/*******************************/
/* Called from  box menu, result needs to be translated to get box number */
{

	text_selected_box1(box_lookup_number(hits[0]),text_selected_box_copy);
}   /* end of text_selected_box */




void text_selected_source(int *hits)
/**********************************/
{
	char *name_ptr;

	if(dbox_menu_field == CD_SOURCE)
	{
		if((current_t->cardex->source = category_menu_hit(hits,&name_ptr)) != 0)
		{
			dbox_setfield(current_t->dbox_card,CD_SOURCE,name_ptr);
			cardfile_update(current_t->fr,NULL,current_t->cardex,0);
		}
	}
	else
	{
		card_add_category(current_t,category_menu_hit(hits,NULL));
	}
}   /* end of text_selected_source */





int text_page_lines(wimp_w w)
/***************************/
{
	int  i;
	int  window_height;
	wimp_wstate wstate;

	wimp_get_wind_state(w,&wstate);
	window_height =wstate.o.box.y1 - wstate.o.box.y0;

	i = text_line_height;
	return((window_height-i) / text_line_height);

}   /* end of text_page_lines */



void text_set_cursor_column(TEXTR *t, int line_start, int n_chars, int prev_c)
/****************************************************************************/
{
	static char up_down_keys[] = {KEY_UP,KEY_DOWN,0x1be,0x1bf,PAGE_UP,PAGE_DOWN};

	if(strchr(up_down_keys,prev_c)==NULL)
		t->x_index = t->cursor_index - line_start;

	if(t->x_index < n_chars)
		t->cursor_index = t->cursor_line_start + t->x_index;
	else
		t->cursor_index = t->cursor_line_start + n_chars - 1;

	if(t->cursor_index < 0)
		t->cursor_index = 0;
}   /* end of text_set_cursor_column */





void text_diagnostic(TEXTR *t)
/****************************/
{
	FILE *f;
	int  i;
	int  count;

	f = fopen_pluto("Diagnostic","w");
	if(f==NULL)
	{
		beep();
		return;
	}

	fprintf(f,"Text type\t%d\ntext_length\t%d\ntext_start\t%d\nbody_end\t%d\nattachments\t%d\nattach #1\t%d\nNlines\t\t%d\n",
			t->text_type,t->text_length,t->text_start,t->text_body_end,t->attachments,t->attach[0].displ,t->n_lines);

	i = t->text_body_end-4;
	fprintf(f,"%d: ",i);
	for(count=0; count<10; count++)
	{
		fprintf(f," %.2x",t->text_base[i+count]);
	}
	fprintf(f,"\n");

	count = t->text_start;
	for(i=0; i<t->n_lines; i++)
	{
		fprintf(f,"  %3d %5d\n",t->line_tab[i],count);
		count += t->line_tab[i];
	}
	fclose(f);
}   /* end of text_diagnostic */



void text_toggle_internet_headers(TEXTR *t)
/*****************************************/
{
	int  scroll_posn;
	wimp_wstate wstate;

	if(t->internet_header == 0)
		beep();

	wimp_get_wind_state(t->w_text,&wstate);
	scroll_posn = wstate.o.y;
	if(t->show_message_headers == 0)
	{
		t->remember_scroll = scroll_posn;
	}

	t->show_message_headers ^= 1;
	dbox_setnumeric(t->dbox_card,CD_HEADER,t->show_message_headers);

	calc_line_tab(t);
	redraw_text_lines(t,0,-1);
	open_article(t,9);

	if(t->show_message_headers == 0)
	{
		if(scroll_posn == 0)
		{
			/* we haven't scrolled since showing the header */
			wimp_get_wind_state(t->w_text,&wstate);
			wstate.o.y = t->remember_scroll;
			wimp_open_wind(&wstate.o);
		}
	}
}   /* end of text_toggle_internet_headers */



void text_menu_send(TEXTR *t, int action)
/***************************************/
{
	switch(action)
	{
	case 1:
		edit_reply(t,1);   /* mail reply */
		break;
	case 2:
		edit_reply(t,2);   /* news followup */
		break;
	case 3:
		edit_reply(t,0x1011);  /* forward by mail */
		break;
	case 4:
		bounce_messages(t->fr,t->cptr);
		break;
	case 5:
		edit_reply(t,0x21);  /* blank mail */
		break;
	case 6:
		edit_reply(t,0x22);  /* blank news */
		break;
	case 7:
		t->cardex->text_anchor = &t->text_base;
		t->cardex->text_length = t->text_length;
		reply_receipt_ack(t->cardex,t->cardex->author,t->cardex->user-1,4);

		/* set replied indicator */
		reply_set_status(t->fr,t->cptr);
		break;
	default:
		edit_reply(t,0);   /* show Reply dialogue box */
		break;
	}
}   /* end of text_menu_send */





void save_text_as(TEXTR *t, char *name)
/*************************************/
{
	FILE *f;
	char path[200];

	sprintf(path,"%s%s",pluto_path,name);
	f = fopen_werr(path,"w",NULL);
	if(f==NULL)
		return;

	fwrite_werr(t->text_base,1,t->text_length,f);
	fclose(f);
}  /* save_text_as */






void key_text_window_edit(int c, TEXTR *t)
/****************************************/
/* for those keys which edit the text */
{
	int  c1;
	int  count;
	int  i;
	int  spell_error;
	char *expansion;
	int  diff;
	int  quote_count;
	int  quote_str_len;
	int  n_inserted;
	int  word_start;
	int  date;
	char buf[64];

	if(isspace(c) && (escape_flag > 0))
		escape_flag--;

	spell_error = 0;

	if((t->allow_edit == 0) || (t->ext_edit))
	{
		if(c > 0xff)
		{
			wimp_processkey(c);
			return;
		}
		beep();
		return;
	}

	t->last_change = alarm_timenow();

	switch(c)
	{
	case 0x03:   /* CTRL-C */
		/* only edit stype EDIT is processed here */
		region_copy(t,0,options.edit_style);
		break;

	case 0x04:   /* CTRL-D */
		/* set date in article viewer */
		if(t->text_type <= X_VIEW2)
		{
			get_current_date_string(&date);
			dbox_setfield(t->dbox_card,CD_DATE,decode_date(date,0xc));
			mark_changed(t);
		}
		break;

	case 0x11:   /* CTRL-Q */
		if(akbd_pollsh())
		{
			/* swap characters at cursor */
			text_swap_chars(t);
		}
		else
		{
			quote_text(t);
		}
		break;

	case 0x13:   /* CTRL-S, swap case */
		c1 = t->text_base[t->cursor_index];
		if(isupper(c1))
			c1 = tolower(c1);
		else
			c1 = toupper(c1);
		t->text_base[t->cursor_index] = c1;
		if(t->cursor_index < (t->text_body_end-1))
			t->cursor_index++;
		mark_changed(t);
		redraw_text_oneline(t,t->cursor_line);
		set_caret_pos(t);
		break;

	case 0x1dd:  /* SHIFT-INSERT */
		paste_clipboard(t);
		break;

	case 0x16:   /* CTRL-V */
		if(options.edit_style == EDIT_EDIT)
			region_copy(t,1,EDIT_EDIT);
		else
			paste_clipboard(t);
		break;

	case 24:   /* ctrl X */
		if(options.edit_style != EDIT_EDIT)
			region_copy(t,0,options.edit_style);

		text_region_delete(t,akbd_pollsh());
		break;

	case KEY_COPY:  /* delete forwards */
		if((t->cursor_index+1) >= t->text_body_end)
			break;   /* reached the end */
		t->cursor_index++;
		text_character_delete(t);
		break;

	case 0x7f:    /* delete */
		if(akbd_pollctl())
		{
			/* ctrl delete, delete line */
			text_delete_line(t);
			mark_changed(t);

			/* flush keyboard buffer */
			os_swi2(0x06,21,0);   /* OS_Byte 21 */
		}
		else
		{
			if(os_version >= OS_v50)
			{
				/* delete forward in later versions */
				if((t->cursor_index+1) >= t->text_body_end)
					break;   /* reached the end */
				t->cursor_index++;
			}
			text_character_delete(t);
		}
		break;

	case 8:     /* delete */
		text_character_delete(t);
		break;

	default:
		/* insert character */
		if(t->allow_edit == 0)
			break;

		if(t->wordwrap == 255)
		{
			/* initilly set at ths value for a forwarded message */
			t->wordwrap = options.reply_wordwrap;
		}

		if(t->region_end > t->region_start)
		{
			if(options.edit_style == EDIT_STANDARD)
			{
				clear_text_region(t);
			}
			else if(options.edit_style == EDIT_WIPEOUT)
			{
				region_copy(t,0,options.edit_style);
				text_region_delete(t,0);
			}
		}

#ifdef deleted
		if(c == 0xa0)
			c = ' ';   /* replace hard-space by space */
#endif

		if(c < ' ')
		{
			if(c == '\r')
				c = '\n';
			else if(c != '\t')
				break;     /* ignore other control characters */

		}
		else if(c > 0xff)
		{
			if(c == 0x18a)   /* tab */
			{
				c = '\t';
			}
			else
			{
				wimp_processkey(c);
				break;
			}
		}
		mark_changed(t);

		n_inserted = 1;

		if(c == '\n')
		{
			/* insert quoting characters if we are breaking a quoted line */
			if((quote_count = is_quoted_text(&t->text_base[t->cursor_line_start])) > 0)
			{
				i = t->cursor_index - t->cursor_line_start;
				quote_str_len = get_quote_string_length(&t->text_base[t->cursor_line_start],NULL);

				if(i <= (quote_str_len+1))
				{
					t->cursor_index = t->cursor_line_start;  /* leave a blank line */
				}
			}
		}


		if(n_inserted > 0)
		{
			if((c1 = c) == '\t')
				c1 = ' ';
			if(move_down(t,t->cursor_index,1)==0)
				t->text_base[t->cursor_index++] = c1;
		}

		diff = 0;
		if(!isalnum(c) && (c != '\''))   /* was isalpha() */
		{
			/* end of word */
			spell_error = 0;

			word_start = t->cursor_index-2;
			while(word_start > 0)
			{
				if(isalnum(c1 = t->text_base[word_start-1]) ||
						((c1 == '\'') && isalnum(t->text_base[word_start-2])))
					word_start--;
				else
					break;
			}

			for(i=0; i<sizeof(buf); i++)
			{
				c1 = t->text_base[word_start+i];
				if((isalnum(c1)) || (c1=='\''))
					buf[i] = c1;
				else
					break;
			}
			if(buf[i-1] == '\'')
				i--;         /* don't include ' at end of word */
			if((buf[i-2] == '\'') && (tolower(buf[i-1])=='s'))
				i -= 2;      /* don't include 's at end of word */
			buf[i] = 0;


			expansion = userdict_lookup(buf);
			if((expansion != 0) && (expansion[0] != 0))
			{
				t->cursor_index = word_start + i + 1;

				/* replace abbreviation */
				diff = spell_replace(t,expansion,word_start,t->cursor_index-1);

				t->cursor_index += diff;
				spell_error = 3;

				if((options.speak_tayt) && (diff < 40))
					speak_string(expansion);  /* don't speak if it's a long text */
			}
			else if((options.check_as_you_type) && (escape_flag == 0))
			{
				if(spell_lookup(t,t->cursor_index-2) == 0)
				{
					spell_error = 1;
					spell_set_word(t,word_start,options.open_on_error);

					if(options.open_on_error)
					{
						spell_invoke(t);
						spell_error=2;
					}

					beep();
				}
			}

			if(!spell_error)
			{
				/* talk-as-you-type */
				if(!isalnum(c) && (options.speak_tayt))
				{
					if(isalnum(c1 = t->text_base[t->cursor_index - 2]) || (c1 == '\''))
					{
						speak_word(t,word_start);
					}
				}
			}
		}

		if((c == '\t') && (spell_error == 2))
			c = ' ';

		if(c == '\t')
		{
			if((i = options.tabs) <= 0) i = 1;

			count = i - ((t->cursor_index - t->cursor_line_start) % i);

			if((count > 0) && (count < i))
			{
				move_down(t,t->cursor_index,count);
				memset(&t->text_base[t->cursor_index],' ',count);
				t->cursor_index += count;
				n_inserted = count;
			}
		}

		if(c == '\n')
		{
			/* insert quoting characters if we are breaking a quoted line */
			if((quote_count > 0) && (!spell_error))
			{
				i = t->cursor_index - t->cursor_line_start;

				if((i > (quote_str_len+2)) && (i < t->line_tab[t->cursor_line]))
				{
					i=0;
					if(t->text_base[t->cursor_index] != ' ')
						i = 1;  /* add an extra space if the next character isn't one */

					if(move_down(t,t->cursor_index,quote_str_len+i)==0)
					{
						memcpy(&t->text_base[t->cursor_index],&t->text_base[t->cursor_line_start],quote_str_len);
						t->cursor_index += quote_str_len;
						if(i != 0)
							t->text_base[t->cursor_index] = ' ';
						t->cursor_index++;
						n_inserted += (quote_str_len+i);
					}
				}
			}
		}

		recalc_line_tab(t,t->cursor_line,n_inserted+diff);
		if(spell_error != 2)
			set_caret_pos(t);

		break;
	}
	t->last_cursor_index = t->cursor_index;
}  /* end of key_text_window_edit */



void text_swap_font(TEXTR *t)
/***************************/
{
	options.text_font_toggle ^= 1;
	options_save();

	text_display_change(&options.d,2);
}   /* end of text_swap_font */




void text_allow_edit(TEXTR *t)
/****************************/
{
	if((t->allow_edit == 0) && (t->cardex->ftype == 1))
	{
		t->allow_edit = 1;
		text_read_article(t,t->cardex,t->cardex->addr,t->cardex->text_offset,t->cardex->text_length,1,1);
	}
	else
	{
		text_reset_header_order(t);
		t->allow_edit = 1;
	}
}   /* end of text_allow_edit */



void external_edit_toggle(TEXTR *t)
/*********************************/
{
	if(t->ole)
		beep();   /* OLE in progress */
	else
	{
		t->ext_edit ^= 1;
		if(t->ext_edit)
			reply_start_OLE(t);

		reply_set_ext_edit(t,3);
	}
}   /* end of external_edit_toggle */




static void text_delete_article(TEXTR *t, int shiftkey)
/**********************************************/
{
	FOLDREC *fr;
	CARD_EXPANDED *cardex;

	speak_stop(t);
	fr = t->fr;
	cardex = t->cardex;

	if(confirm_delete_article(t,cardex))
	{
		if(cardfile_remove(t,cardex->card_ptr,BOX_BIN) < 0)
			return;

		cardex->card_ptr = NULL;
		cardex->header_changed = 0;
	}

	if(t->text_type == X_VIEW)
	{
		if(card_open_next(fr,5+shiftkey,1,t->just_removed) != 0)
			close_article_window(t);
	}
}   /* end of text_delete_article */





int key_text_window1(int c, TEXTR *t)
/********************************/
/* Key commands that can be obeyed in the Card window or the Text window */
{
	int shiftkey;
	int  box;
	CARD_EXPANDED *cardex;

	shiftkey = akbd_pollsh() << 1;
	cardex = t->cardex;

	switch(c)
	{
	case 0x02:   /* CTRL-B */
		if(t->text_type <= X_VIEW2)
		{
			if(check_lock_lists(0xffff) != 0)
				break;

			if(akbd_pollsh())
				fetch_full(t->fr,t->cptr,1);  /* cancel fetch-full */
			else
			{
				if(fetch_full(t->fr,t->cptr,0) != 0)
					beep();
			}
			redraw_list_lines(NULL,0);
		}
		return(1);

	case 0x05:   /* CTRL-E */
		if(akbd_pollsh())
		{
			text_allow_edit(t);

			if(t->text_type > X_VIEW2)
			{
				external_edit_toggle(t);
			}
		}
		else if(t->text_type <= X_VIEW2)
		{
			cardex->status = STATUS_READ;
			cardfile_update(t->fr,t,cardex,1);
		}
		else
		{
			set_button_state(t->w_card,2,0,wimp_ISELECTED);
		}
		return(1);

	case 0x06:  /* CTRL-F */
		if(akbd_pollsh())
		{
			text_swap_font(t);
			return(1);
		}
		break;

	case 0x07:  /* CTRL-G */
		if(akbd_pollsh())
		{
			text_remove_tags(t);
			return(1);
		}
		break;

	case 0x09:  /* CTRL-I */
		if(t->text_type == X_VIEW)
		{
			thread_change_offset(t->fr,akbd_pollsh());
			return(1);
		}
		break;

	case 0x0b:  /* CTRL-K, delete article */
		if(t->text_type <= X_VIEW2)
		{
			card_add_binned_list(t->cptr);
			text_delete_article(t,shiftkey);
		}
		return(1);;

	case 12:   /* Ctrl L */
		if(t->text_type <= X_VIEW2)
		{
			cardex->status = STATUS_LOCKED;

			cardfile_update(t->fr,t,cardex,1);
		}
		else
		{
			set_button_state(t->w_card,2,1,wimp_ISELECTED);
		}
		return(1);

	case 14:   /* ctrl N */
		if(t->text_type == X_VIEW)
		{
			speak_stop(t);
			card_open_next(t->fr,1+shiftkey,1,t->just_removed);
		}
		return(1);

	case 16:   /* ctrl P */
		if((shiftkey) && (t->text_type > X_VIEW2))
		{
			reply_post_message(t);
		}
		else
		{
			if(t->text_type == X_VIEW)
			{
				speak_stop(t);
				card_open_next(t->fr,0+shiftkey,1,t->just_removed);
			}
		}
		return(1);

	case 18:   /* ctrl R */
		if(t->text_type <= X_VIEW2)
		{
			box = box_table[cardex->docbox].archive_to;
			if((box != 255) && (box != BOX_BIN) && (box != cardex->docbox))
			{
				current_t = t;
				text_selected_box1(box,box_table[cardex->docbox].flags & BOX_ARCHIVE_COPY);
				if((t->text_type==X_VIEW) && (shiftkey))
				{
					speak_stop(t);
					card_open_next(t->fr,1+shiftkey,1,t->just_removed);
				}
			}
			else
			{
				beep();
			}
		}
		return(1);

	case 20:   /* ctrl T */
		if((t->text_type == X_VIEW) && (t->fr->display_levels > 1))
		{
			speak_stop(t);   /* skip thread, then next unread */
			skip_thread(t->fr,-1);
			card_open_next(t->fr,9+shiftkey,1,t->just_removed);
		}
		return(1);

	case 25:  /* CTRL-Y */
		if(shiftkey)
		{
			/* CTRl-SHIFT-Y - encrypt */
			if(t->text_type <= X_VIEW2)
			{
				if(pgp_encrypt(t) == 0)
				{
					text_interpret_article(t,t->cardex,1);
					mark_changed(t);
				}
			}
			else
			{
				/* just set encrypt and sign flags */
				if(t->art_out != NULL)
					t->art_out->pgp |= 3;
			}
		}
		else if(t->text_type <= X_VIEW2)
		{
			text_reset_header_order(t);
			pgp_decode(t);
			text_interpret_article(t,cardex,1);
			open_article(t,0x83);
			mark_changed2(t);
		}
		return(1);

	case 0x1b:   /* escape */
		if(t == textr_spellcheck)
		{
			close_spell_window(t);
		}
		if(t->text_type <= X_VIEW2)
		{
			reply_dbox_close();
		}

		if(get_button_state(t->w_card,t->lips_icon))
		{
			c = t->speak_end;
			toggle_speak(t,0);
			t->speak_end = c;
		}
		else
		{
			if(t->speak_end > 0)
				toggle_speak(t,1);
		}
		escape_flag = 2;
		return(1);

	case F_KEY+0:
		print_text_dialog(t,cardex,0);
		return(1);

	case F_KEY_CTRL+2:
		if(t->text_type > X_VIEW2)
		{
			if(reply_check_changed(t))
				return(1);
		}
		text_close_window(t);
		return(1);

	case F_KEY_SHIFT+3:
	case F_KEY_CTRL+3:
		if(t->text_type <= X_VIEW2)
			text_save_article(t);
		else
			reply_save_draft(t,0);
		return(1);

	case F_KEY+3:
		save_text(t,0);
		return(1);

	case F_KEY_SHIFT+6:
		current_t = t;
		text_selected_box_copy = 1;
		make_boxes_menu((t->cptr->date_box >> 26) + 0x2800,NULL);
		dbox_menu(wmenu_boxes_names,text_selected_box,NULL);
		return(1);

	case F_KEY+6:
		current_t = t;
		make_boxes_menu((t->cptr->date_box >> 26) + 0x1900,NULL);
		dbox_menu(wmenu_boxes_names,text_selected_box,NULL);
		return(1);

	case F_KEY+7:   /* add to address book */
		if(t->text_type <= X_VIEW2)
			addrlist_add_email(cardex->author,0);
		else
			addrlist_open(1);
		return(1);

	case F_KEY_SHIFT+8:
		msgid_list_parents(t);
		break;

	case F_KEY+9:
		spell_invoke(t);
		return(1);
	}
	return(0);
}   /* end of key_text_window1 */








void key_text_window(wimp_eventdata *d, TEXTR *t)
/***********************************************/
{
	int prev_c;
	int  n_chars;
	int  n_lines;
	int  line_start;
	int  shiftkey;
	int  i;
	int  window_height;
	wimp_wstate wstate;
	static int expecting_w = 0;

	CARD_EXPANDED *cardex;

	static int  c = 0;

	n_chars = t->line_tab[t->cursor_line];
	shiftkey = akbd_pollsh() << 1;
	cardex = t->cardex;

	text_selected_box_copy = 0;

	prev_c = c;
	c = d->key.chcode;

	if(expecting_w)
	{
		expecting_w = 0;
		switch(c)
		{
		case 18:   /* R */
			i = 1;
			break;
		case 6:    /* F */
			i = 2;
			break;
		case 4:   /* D */
			i = 3;
			break;
		case 2:   /* B */
			i = 4;
			break;
		case 13:   /* M */
			i = 5;
			break;
		case 14:   /* N */
			i = 6;
			break;
		case 21:   /* U */
			i = 7;
			break;
		default:
			i = 0;
			break;
		}
		text_menu_send(t,i);
		return;
	}

	if(t->text_type == X_VIEW)
	{
		/* check for numeric keypad */
		switch(c)
		{
		case '4':
			if(is_keypressed(122))
				c = 16;   /* CTRL P */
			break;
		case '6':
			if(is_keypressed(26))
				c = 14;   /* CTRL N */
			break;
		case '+':
			if(is_keypressed(58))
				c = 20;   /* CTRL T */
			break;
		case '3':
			if(is_keypressed(108))
				c = PAGE_DOWN;   /* Page down */
			break;
		case '9':
			if(is_keypressed(43))
				c = PAGE_UP;   /* Page up */
			break;
		}
	}

	if(key_text_window1(c,t))
		return;   /* commands allowed in the Card window also */

	switch(c)
	{
#ifdef deleted
	case 4:
		if(shiftkey)
			text_diagnostic(t);
		break;
#endif

	case KEY_RIGHT:
		if(t->cursor_index < (t->text_body_end))
			t->cursor_index++;
		set_caret_pos(t);
		break;

	case KEY_LEFT:
		if(t->cursor_index > 0)
			t->cursor_index--;
		set_caret_pos(t);
		break;

	case 0x1be:   /* shift-ctrl-down */
		scroll_by_y(t->w_text,-text_line_height);
		/* drop through to next case */
	case KEY_DOWN:
		if(t->cursor_line < (t->n_lines-1))
		{
			line_start = t->cursor_line_start;

			t->cursor_line++;
			t->cursor_line_start += n_chars;

			n_chars = t->line_tab[t->cursor_line];

			text_set_cursor_column(t,line_start,n_chars,prev_c);
		}
		set_caret_pos(t);
		break;

	case 0x1bf:   /* shift-ctrl-up */
		scroll_by_y(t->w_text,text_line_height);
		/* drop through to next case */
	case KEY_UP:
		if(t->cursor_line > 0)
		{
			line_start = t->cursor_line_start;

			n_chars = t->line_tab[--t->cursor_line];
			t->cursor_line_start -= n_chars;

			text_set_cursor_column(t,line_start,n_chars,prev_c);
			set_caret_pos(t);
		}
		else
		{
			/* move up from the text area into the header */
			if(t->text_type == X_MAIL)
			{
				if(t->button_bar_expanded)
					i = 12;
				else
					i = 9;
			}
			else
			{
				if(t->button_bar_expanded)
					i = 11;
				else
					i = 10;
			}
			set_input_focus(t->w_card,i,0);
		}
		break;

	case 0x1ac:   /* CTRL-LEFT */
		t->cursor_index = t->cursor_line_start;
		set_caret_pos(t);
		break;


	case KEY_COPY:
		if(os_version < OS_v50)
		{
			/* ealier versions, delete forwards */
			key_text_window_edit(c,t);
			break;  /* deal with this in key-edit section */
		}
		/* drop through to CTRL_RIGHT */

	case 0x1ad:   /* CTRL-RIGHT */
		n_chars = t->line_tab[t->cursor_line];
		t->cursor_index = t->cursor_line_start + n_chars-1;
		set_caret_pos(t);
		break;



	case 0x19c:   /* SHIFT-LEFT */
		cursor_prev_word(t);
		while(t->cursor_index < t->cursor_line_start)
		{
			t->cursor_line--;
			n_chars = t->line_tab[t->cursor_line];
			t->cursor_line_start -= n_chars;
		}
		set_caret_pos(t);
		break;

	case 0x19d:   /* SHIFT-RIGHT */
		cursor_next_word(t);
		while((t->cursor_index - t->cursor_line_start) >= n_chars)
		{
			t->cursor_line++;
			t->cursor_line_start += n_chars;
			t->cursor_index = t->cursor_line_start;
		}
		set_caret_pos(t);
		break;

	case PAGE_UP:
		if(t->cursor_line > 0)
		{
			line_start = t->cursor_line_start;
			n_lines = text_page_lines(t->w_text);
			if((t->cursor_line - n_lines) < 0)
				n_lines = t->cursor_line;

			while(n_lines-- >= 0)
			{
				t->cursor_line_start -= t->line_tab[--t->cursor_line];
			}

			n_chars = t->line_tab[t->cursor_line];
			text_set_cursor_column(t,line_start,n_chars,prev_c);
		}

		wimp_get_wind_state(t->w_text,&wstate);
		window_height =wstate.o.box.y1 - wstate.o.box.y0;
		wstate.o.y += window_height;
		set_caret_pos2(t);
		open_article_window(t,&wstate.o,0);

		break;

	case PAGE_DOWN:
		if(t->cursor_line < (t->n_lines-1))
		{
			line_start = t->cursor_line_start;

			n_lines = text_page_lines(t->w_text);
			if((t->cursor_line + n_lines) >= t->n_lines)
				n_lines = t->n_lines- t->cursor_line - 1;

			while(n_lines-- >= 0)
			{
				t->cursor_line_start += t->line_tab[t->cursor_line++];
			}

			n_chars = t->line_tab[t->cursor_line];
			text_set_cursor_column(t,line_start,n_chars,prev_c);
		}

		wimp_get_wind_state(t->w_text,&wstate);
		window_height =wstate.o.box.y1 - wstate.o.box.y0;
		wstate.o.y -= window_height;
		set_caret_pos2(t);
		open_article_window(t,&wstate.o,0);

		break;

	case KEY_CT_COPY:
	case KEY_CT_DOWN:
		scroll_to_y(t->w_text,-t->n_lines*text_line_height);
		t->cursor_line = t->n_lines-1;
		t->cursor_line_start = t->text_body_end - t->line_tab[t->cursor_line];
		t->cursor_index = t->text_body_end-1;
		set_caret_pos(t);
		break;

	case KEY_HOME:
		if((os_version >= OS_v50) && !akbd_pollctl())
		{
			/* start of line */
			t->cursor_index = t->cursor_line_start;
			set_caret_pos(t);
			break;
		}
		/* else drop through to CTRL_UP */

	case KEY_CT_UP:
		scroll_to_top(t->w_text,-1);
		t->cursor_line = 0;
		t->cursor_line_start = t->text_start;
		t->cursor_index = t->text_start;
		set_caret_pos(t);
		t->prev_scroll_posn = 0;
		break;

	case 0x01:   /* CTRL-A */
		t->region_start = t->text_start;
		t->region_end = t->text_body_end-1;
		redraw_text_lines(t,0,-1);
		break;

	case 0x1ed:  /* COPY-INSERT */
		region_copy(t,0,0);
		break;

	case 0x03:   /* CTRL-C */
		if((options.edit_style == EDIT_EDIT) && !t->allow_edit)
			beep();
		else
			region_copy(t,0,options.edit_style);
		break;

	case 0x06:   /* CTRL-F */
		format_quoted_text(t);
		break;

	case 0x08:   /* CTRL-H */
		if(akbd_pollctl())
		{
			if(t->text_type <= X_VIEW2)
				text_toggle_internet_headers(t);
		}
		else
			key_text_window_edit(c,t);
		break;

	case 13:   /* CTRL M this is also RETURN */
		if(akbd_pollctl() && (t->text_type <= X_VIEW2))
		{
			cardex->status = STATUS_MARKED;
			cardfile_update(t->fr,t,cardex,1);
		}
		else
		{
			key_text_window_edit(c,t);
		}
		break;

	case 15:   /* ctrl O */
		text_rot13(t);
		break;

		/* ???? */
		if(shiftkey)
			list_close_level(t->fr);
		break;

	case 21:   /* CTRL U, mark as unread */
		if(t->text_type <= X_VIEW2)
		{
			cardex->status = STATUS_UNREAD;
			cardex->replied = 0;
			cardfile_update(t->fr,t,cardex,1);
			card_changed_status(t->cptr);
		}
		break;

	case 23:   /* ctrl W */
		if(t->text_type <= X_VIEW2)
		{
			expecting_w=1;  /* expect another character */
		}
		break;

	case 26:   /* ctrl Z */
		null_events(2,0);  /* turn off drag, in case it's been left on */

		if(shiftkey)
		{
			/* CTRL-SHIFT-Z  purge clipboard */
			if(clipboard != NULL)
			{
				flex_free((flex_ptr)&clipboard);
				clipboard_size = 0;
			}
		}
		else
		{
			clear_text_region(t);
		}
		break;


	case F_KEY+4:
		text_find_string(t);
		break;

	case F_KEY_SHIFT+4:
		if(text_find_string_next(t,search_text_string,search_case_sensitive,search_regex_flag) < 0)
			beep();   /* not found */
		break;

#ifdef deleted
	case F_KEY+5:
		show_card_last_displayed();
		break;
#endif

	case F_KEY+8:    /* start speaking */
		toggle_speak(t,1);
		break;


	default:
		key_text_window_edit(c,t);
		break;
	}

}   /* end of key_text_window */




void text_window_handler(wimp_eventstr *e, void *handle)
/******************************************************/
{
	int  more;
	wimp_redrawstr r;
	TEXTR *t;

	t = (TEXTR *)handle;

	switch (e->e)
	{
	case wimp_EREDRAW:
		r.w = e->data.o.w;
		wimp_redraw_wind(&r, &more);

		while(more)
		{
			/* display the text */
			if(display_text_window(t,&r) < 0)
				return;   /* return if error during redraw */

			wimp_get_rectangle(&r, &more);
		}
		break;

	case wimp_EPTRLEAVE:
		visdelay2_leave();
		break;

	case wimp_EPTRENTER:
		visdelay2_enter();
		break;

	case wimp_EOPEN:
		open_article_window(t,&e->data.o,0);
		break;

	case wimp_ECLOSE:  /* Pass on close request */
		text_window_close(t);
		wimp_close_wind(e->data.o.w);
		break;

	case wimp_EBUT:
		click_text_window(t,&e->data);
		break;

	case wimp_EKEY:
		key_text_window(&e->data,t);
		break;

	case wimp_ESCROLL:
		break;
		scroll_window(&e->data);
		break;

	case wimp_EUSERDRAG:
		null_events(2,0);     /* turn off for drag */
		break;


	case wimp_ESEND:
	case wimp_ESENDWANTACK:
		switch(e->data.msg.hdr.action)
		{
		case wimp_MDATASAVE:
			xferrecv_wimpscrap(e);
			break;

		case wimp_MDATALOAD:     /* insert data */
			insert_text_file(t);
			break;
		}
		break;

	default:   /* Ignore any other event */
		break;
	}
}   /* end of text_window_handler */




void open_article(TEXTR *t, int control)
/**************************************/
/* control bit 0  full size
               1  keep button bar position
               2  open blank article
               3  restrict lower y
               4  open at saved position
               5  keep size
               6  adjust y0 for button bar height
               7  don't move to front
               8  don't grab input focus
               9  keep cursor position
*/

{
	int  y;
	int  min;
	int  height;
	TEXTR *t2;
	int  i;
	int  posn;
	int  cursor_index;
	wimp_wstate wstate;
	wimp_wstate wstate2;
	wimp_wstate wstate_c;

#define N_AV_POSNS 6
	char positions[N_AV_POSNS];

	dbox_showstatic(t->dbox_card);

	wimp_get_wind_state(t->w_text,&wstate);
	height = wstate.o.box.y1 - wstate.o.box.y0;

	cursor_index = t->cursor_index;

	if((control & 0x80) == 0)
		wstate.o.behind = -1;

	if(control & 2)
	{
		wimp_get_wind_state(t->w_card,&wstate_c);
		wstate.o.box.y1 = wstate_c.o.box.y1 - t->button_bar_height;
	}
	if(control & 1)
	{
		/*      wstate.o.box.y1 = y_screen_dim;  */

		if(control & 0x10)
		{
			wstate.o.box.x0 = options.position_x[0];
			wstate.o.box.x1 = wstate.o.box.x0 + options.position_width[0];
			wstate.o.box.y1 = options.position_y[0];
			wstate.o.box.y0 = options.position_y0[0];

			if(control & 0x40)
				wstate.o.box.y1 -= t->button_bar_height;

			if(t->text_type == X_VIEW2)
			{
				/* don't open at the same place as another window */
				memset(positions,0,sizeof(positions));
				for(i=0; i<N_TEXT_DATA; i++)
				{
					if(((t2 = text_data_record[i]) != NULL) && (t2 != t))
					{
						wimp_get_wind_state(t2->w_text,&wstate2);
						if(wstate2.o.box.x0 != options.position_x[0])
							continue;

						for(posn=1; posn<N_AV_POSNS; posn++)
						{
							if(wstate2.o.box.y1 == (wstate.o.box.y1 - scroll_bar_height*posn))
								positions[posn] = 1;
						}
					}
				}

				/* find the lowest free position, so it doesn't obscure the other title bars */
				for(posn=N_AV_POSNS-1; posn>0; posn--)
				{
					if(positions[posn] != 0)
						break;
				}
				wstate.o.box.y1 -= (scroll_bar_height*(posn+1));
			}

		}

		y = -t->workarea.box.y0;
		if(control & 4)
		{
			if(y > 400) y = 400;
			if(y < t->extent_y) y = t->extent_y;
		}

		/* keep the y position of the top of the window */
		if(y > (wstate.o.box.y1 - scroll_bar_height))
			y = wstate.o.box.y1 - scroll_bar_height;

		if(control & 0x20)
			y = height;

		wstate.o.box.y0 = wstate.o.box.y1 - y;

		if(control & 8)
		{
			if((options.restrict_height==1) || preserve_iconbar())
				min = 160;
			else
				min = 0;

			/* don't cover icon bar */
			if(wstate.o.box.y0 < min)
				wstate.o.box.y0 = min;
		}
	}

	wstate.o.y = wstate.o.box.y1 - wstate.o.box.y0;
	open_article_window(t,&wstate.o,0);

	if((control & 0x100) == 0)
		set_focus(t->w_text);

	if(control & 0x200)
	{
		/* keep cursor position the same */
		t->cursor_index = cursor_index;
		set_caret_pos(t);
	}

	/* set spellcheck button state */
	if(t->text_type <= X_VIEW2)
		set_icon_state(t->w_card,19,options.check_as_you_type);
	else
		set_icon_state(t->w_card,15,options.check_as_you_type);

}   /* end of open_article */






void get_quoted_name(char *out,char *str, int max)
/************************************************/
{
	int  i;
	int  c;
	int  quoted=0;
	char buf[128];

	while(isspace(*str)) str++;
	if(str[0] == '"')
	{
		quoted=1;
		str++;
	}

	i=0;
	while(((c = str[i]) > 0) && (c != '"') && (c != ';') && (i < (sizeof(buf)-1)))
	{
#ifdef deleted
		if(quoted & (c == ' '))  c = 0xa0;  /* replace space by hard space */
#endif
		buf[i++] = c;
	}
	buf[i]=0;

	decode_iso8859(buf,0);

	buf[max-1] = 0;
	strcpy(out,buf);
}   /* end of get_quoted_name */



void text_autosave2(TEXTR *t)
/*************************/
{
	char fname1[36];
	char fname2[36];
	static char *fname = "backup.AutoSav";


	if(t->text_type > X_VIEW2)
	{
		reply_save_draft(t,1);
	}
	else
	{
		sprintf(fname1,"%s%s",pluto_path,fname);
		sprintf(fname2,"%s%s2",pluto_path,fname);

		remove(fname2);
		file_move(fname1,fname2);
		save_text_as(t,fname);
	}
}   /* end of text_autosave2 */



void text_autosave(int time, void *handle)
/****************************************/
{
	TEXTR *t;

	t = (TEXTR *)handle;

	if(text_record_check(t) == -1)
		return;   /* no longer an active record */

	if(options.autosave == 0)
		return;

	if(t->allow_edit==0)
	{
		return;
	}

	if((time != 0) && t->changed && (t->last_change > t->last_save) &&
			(t->text_length > 4) &&
			((time - t->last_change) > 250) && ((time - t->last_save) > (options.autosave * 6000)))
	{
		text_autosave2(t);
		t->last_save = time;
	}
	/* set next alarm time */
	if(alarm_anypending(handle) == 0)
		alarm_set(alarm_timenow() + 200, text_autosave, handle);  /* 2 seconds */
}   /* end of text_autosave */




void attach_set_sprite(TEXTR *t, int ix, int ftype)
/*************************************************/
{
	os_error *error;
	char sprite_name[12];

	sprintf(sprite_name,"file_%.3x",ftype);

	/* is there a sprite for this filetype ? */
	error = os_swi3(0x400e9 + os_X,40,0,(int)sprite_name);   /* Wimp_SpriteOp */
	if(error)
		strcpy(sprite_name,"file_xxx");

	sprintf(t->attach[ix].sprite,"S%s",sprite_name);
}   /* end of attach_set_sprite */







void show_attachment_icons(TEXTR *t)
/**********************************/
/* Place the attachment icons in the attachments window,
   and set its extent */
{
	int  ix;
	int  rows;
	wimp_redrawstr  w_workarea;

	if(t->attachments == 0)
	{
		wimp_close_wind(t->w_attach);
		return;
	}

	for(ix=0; ix < t->num_attach; ix++)
	{

		if(ix < t->attachments)
		{
			attach_set_sprite(t,ix,t->attach[ix].ftype);

			wimp_set_icon_state(t->w_attach,ix,0x1700a12b | (t->attach[ix].selected << 21), (int)0xffffffff);
		}
		else
		{
			wimp_set_icon_state(t->w_attach,ix,0x1780a18b,(int)0xffffffff);  /* bits 23 and 7 to delete icon */
		}
	}

	rows = (t->attachments+4) / 5;
	memset(&w_workarea,0,sizeof(w_workarea));
	w_workarea.w = t->w_attach;
	w_workarea.box.y0 = -(rows * 116);
	w_workarea.box.x1 = 1300;
	wimp_set_extent(&w_workarea);
}   /* end of show_attachment_icons */






void text_remove_attachments(TEXTR *t)
/************************************/
{
	if(t->attachments==0)
	{
		beep();
		return;
	}

	text_reset_header_order(t);

	t->text_length = t->text_body_end;
	t->attachments = 0;
	mark_changed(t);
	show_attachment_icons(t);

	text_save_article(t);
}   /* end of text_remove_attachments */





int text_find_sig(char *start, int length)
/****************************************/
/* Look for sig. in text */
{
	int  i;

	length -= 4;

	for(i=0; i<length; i++)
	{
		if(start[i] == '\n')
		{
			if(memcmp(start+i+1,"-- \n",4)==0)
				return(i+1);
			if(memcmp(start+i+1,"--=20\n",6)==0)
				return(i+1);
		}
	}
	return(0);   /* not found */
}   /* end of text_find_sig */




void text_content_type_line(TEXTR *t, int start, int end, char *buf, int buflen)
/******************************************************************************/
{
	int  i;
	char *argument;
	char *p;
	char *p1;
	int  length;
	int  value;

	length = end - start;
	if(length >= buflen)
		length = buflen-1;

	argument = &t->text_base[start];
	for(i=0; i<length; i++)
		buf[i] = tolower(argument[i]);
	buf[i] = 0;

	if(t->charset == 0)
	{
		if((p = strstr(buf,"iso-8859-")) != NULL)
		{
			if(sscanf(&p[9],"%d",&value))
				t->charset = value;
		}
		else if(strstr(buf,"utf-8")!=NULL)
		{
			t->charset = 102;
		}
	}

	if((p = strstr(buf,"multipart/alternative")) != NULL)
	{
		t->multi_alternative = 1;
	}
	if((p = strstr(buf,"multipart/encrypted")) != NULL)
	{
		t->multi_encrypted = 1;
	}

	if(n_boundaries >= (N_BOUNDARIES-1))
		return;

	p = &argument[12];
	for(i=0; i<120; i++)
	{
		if((p[0]==0) || ((p[0]=='\n') && (!isspace(p[1]))))
			break;

		if(memcmp_lc(p,"boundary",8)==0)
		{
			p += 8;
			if(isspace(*p)) p++;
			if(*p == '=')
			{
				p++;
				if(isspace(*p)) p++;

				if(*p=='"') p++;
				p1 = strpbrk(p,"\";\n");
				length = p1-p;
				if(length >= N_BOUNDARY_LEN)
					length = N_BOUNDARY_LEN-1;
				memcpy(boundary_string[n_boundaries],p,length);
				boundary_string[n_boundaries][length] = 0;
				boundary_string_len[n_boundaries]=length;

				n_boundaries++;
				break;
			}
		}
		p++;
	}
}   /* end of text_content_type_line */




void text_interpret_internet_header(TEXTR *t, CARD_EXPANDED *cardex, int start1)
/******************************************************************************/
{
	int  i;
	int  keyword;
	int  start;
	int  argument2;
	int  arg_end;
	char *argument;
	int  c;
	int  hours, mins, day, hrsmins;
	int  minutes;
	int  newsg_start=0;
	int  newsg_end;
	int  followup_source;
	int  ignore;
	MNEM_TAB2 *m;
	char *p;
	char *p1;
	char buf[200];
	char from_addr[80];

	start = start1;
	t->minutes = 0;  /* not set */
	t->encoding = 0;
	n_reorder = 0;
	from_addr[0] = 0;

	while((keyword = interpret_article_key(&t->text_base,&start,t->text_length,&argument2,&arg_end)) >= 0)
	{
		argument = &t->text_base[argument2];
		ignore = 0;

		switch(keyword)
		{
		case 1:    /* From */
			c = t->text_base[arg_end];
			t->text_base[arg_end] = 0;
			strncpy0(from_addr,reply_extract_email_addr(&t->text_base[argument2],3),sizeof(from_addr));
			t->text_base[arg_end] = c;
			break;

		case 12:   /* Reply-to */
			c = t->text_base[arg_end];
			t->text_base[arg_end] = 0;
			p = reply_extract_email_addr(&t->text_base[argument2],3);
			t->text_base[arg_end] = c;

			if((cardex->status_other2 & STATUS_BIT_MAILLIST) || (strcmp(p,from_addr)==0))
				ignore = 1;   /* Mailing list, or Reply-to is the same as From */

			break;

		case 3:    /* Date */
			memcpy(buf,&argument[16],11);
			buf[12] = 0;
			if((t->cptr != NULL) && (sscanf(buf,"%d:%d:%d",&hours,&minutes,&i)==3))
			{
				i = t->cptr->date_box & 0x3ffff;
				day = i / 576;
				hrsmins = i - (day * 576);
				hours = hrsmins / 24;
				mins = (hrsmins - (hours * 24));   /* x 2.5 minutes */

				if(mins == ((minutes*2)/5))
				{
					t->minutes = minutes + 1;
				}
			}
			break;

		case 4:    /* Newsgroups */
			newsg_start = argument2;
			newsg_end = arg_end;

			t->n_newsgroups = 1;
			for(i=argument2; i<arg_end; i++)
			{
				if(((c = t->text_base[i]) == ',') || (c == '\n'))
					t->n_newsgroups++;
			}

			if(t->n_newsgroups == 1)
			{
				p = expand_source(cardex->source,0);
				if(memcmp(&t->text_base[argument2],p,strlen(p))==0)
					ignore = 1;
			}
			break;

		case 11:    /* Followup-To */
			i = arg_end - argument2;
			if(i >= sizeof(buf))  i = sizeof(buf)-1;
			memcpy(buf,argument,i);
			buf[i] = 0;

			t->followup_set = 1;
			p = buf;
			for(;;)
			{
				/* examine each newsgroup in the followups-to: line */
				p1 = strchr(p,',');
				if(p1 != NULL)
					*p1 = 0;

				followup_source = category_lookup(&src_fr,p,0);
				if(followup_source == cardex->source)
				{
					t->followup_set = 0;
					break;
				}

				if(p1 == NULL)
					break;
				p = p1 + 1;
			}
			break;

		case 19:   /* Content-Transfer-Encoding */
			if(memcmp_lc(argument,"quoted-printable",16)==0)
			{
				t->encoding = ENCODE_QUOTED;
			}
			else if(memcmp_lc(argument,"rich-text",9)==0)
			{
				t->encoding = ENCODE_RICH;
			}
			break;

		case 20:   /* Content-Type */
			/*         text_mime_content_type(t,a,argument2); */

			text_content_type_line(t,argument2,arg_end,buf,sizeof(buf));
			break;
		}

		/* look for header lines to be reordered to display within the text window */
		m = header_lines;
		while(m->mnem != NULL)
		{
			p = &t->text_base[start1];
			if(memcmp_lc(p,m->mnem,m->len) == 0)
			{
				if((m->value >= 2) && (n_reorder < (N_REORDER-1)))
				{
					if((ignore == 0) || (m->value == 3))
					{
						reorder_pos[n_reorder] = start1;
						reorder_len[n_reorder++] = start - start1;
					}
				}
			}
			m++;
		}
		start1 = start;
	}
	reorder_pos[n_reorder] = 0x7fffffff;
}   /* end of text_interpret_internet_header */



int text_interpret_attachment(TEXTR *t, int a, int start1, int *encoding, int *ftype)
/***********************************************************************************/
{
	int  i;
	int  keyword;
	int  start;
	int  argument2;
	int  arg_end;
	char *argument;
	int  attachment = 1;
	char *p;
	char buf[200];

	start = start1;
	*encoding = ENCODE_TEXT;
	t->attachments++;

	t->attach[a].content_id = 0;

	while((keyword = interpret_article_key(&t->text_base,&start,t->text_length,&argument2,&arg_end)) >= 0)
	{
		argument = &t->text_base[argument2];

		switch(keyword)
		{
		case 19:   /* Content-Transfer-Encoding */
			if(memcmp_lc(argument,"quoted-printable",16)==0)
			{
				*encoding = ENCODE_QUOTED;
			}
			else if(memcmp_lc(argument,"rich-text",9)==0)
			{
				*encoding = ENCODE_RICH;
			}
			else if(memcmp_lc(argument,"base64",6)==0)
			{
				*encoding = ENCODE_BASE64;
			}
			else if(memcmp_lc(argument,"uuencode",8)==0)
			{
				*encoding = ENCODE_MIMEUUE;
			}
			else if(memcmp_lc(argument,"x-uuencode",8)==0)
			{
				*encoding = ENCODE_MIMEXUUE;
			}
			break;

		case 20:   /* Content-Type */
			*ftype = text_mime_content_type(t,a,argument2,arg_end);
			text_content_type_line(t,argument2,arg_end,buf,sizeof(buf));

			if(*ftype == -2)
			{
				t->attachments--;
				return(0);
			}
			break;

		case 21:   /* Content-Disposition */
			i = arg_end - argument2;
			if(i >= sizeof(buf))  i = sizeof(buf)-1;

			memcpy(buf,argument,i);
			buf[i]=0;
			if((argument = strstr(buf,"filename=")) != NULL)
			{
				get_quoted_name(t->attach[a].name,argument+9,sizeof(t->attach[a].name));
			}
			break;

		case 34:  /* Content-ID */
			p = get_reference(&t->text_base,argument2,arg_end,1);
			if(p != NULL)
			{
				t->attach[a].content_id = gethash32(p);
			}
			break;
		}
	}
	return(attachment);
}   /* end of text_interpret_attachment */




void text_interpret_article(TEXTR *t, CARD_EXPANDED *cardex, int display)
/***********************************************************************/
/* display=0  don't display, just analyze
           1  re-open with the same size and position
           2  open for the first time
*/
{
	char *p;
	int  i;
	int  c;
	int  ix;
	int  a;
	char *p2;
	char *p3;
	int  ftype;
	int  start_attachments;
	int  prev_attachment;

	int  start;
	int  posix;
	int  endix;
	int  transfer_encoding=0;
	int  mime_attachments=0;
	int  pgp_flag = 0;
	int  encrypted = 0;
	int  not_uue;
	int  yenc_attachment = 0;

	int  end2;
	int  reduction;
	int  boundary;
	wimp_wstate wstate;

	int  len;
	int  offset;
	char head_buf[1000];  /* to reorder header lines */

	if(display)
		wimp_get_wind_state(t->w_text,&wstate);

	n_boundaries = 0;
	t->boundary_string_len = 0;
	t->cursor_index = 0;
	t->cursor_line = 0;
	t->cursor_line_start = 0;
	t->region_start = t->region_end = 0;
	t->sig_start = 0x7fffffff;
	t->spell_error_index = -1;
	t->charset = 0;
	t->multi_alternative = 0;
	t->n_newsgroups = 0;
	t->followup_set = 0;
	t->encoding = 0;

	replace_control_chars(t,0,t->text_length);

	p = t->text_base;

	t->internet_header = 0;
	t->attachments = 0;

	if(memcmp(p,"-----BEGIN PGP ",15) == 0)
		encrypted = 1;  /* encrypted message */

	/* look for internet message header */

	if((cardex->flags & INET_HDR_MASK) && (!encrypted))
	{
		if(t->text_base[0] == '\n')
			i = 1;
		else
			i = 0;
		text_interpret_internet_header(t,cardex,i);


		/* Internet header has been kept, look for double NL to end header */
		if((cardex->status_other & STATUS_BIT_HDR_ONLY) && (cardex->status != STATUS_HIDDEN))
		{
			t->internet_header = t->text_length;

			for(i=t->text_length-8; (i>0) && (i > t->text_length-30); i--)
			{
				if((memcmp(t->text_base+i,"\nBytes: ",8)==0) || (memcmp(t->text_base+i,"\nLines: ",8)==0))
				{
					t->internet_header = i+1;
				}
			}
		}
		else
		{
			for(i=0; i<t->text_length; i++)
			{
				if((p[i] == '\n') && (p[i+1] == '\n'))
				{
					t->internet_header = i+2;
					break;
				}
			}
		}
	}

	t->text_start = t->internet_header;


	/* look for attachments */
	start_attachments = t->text_length;
	prev_attachment = -1;

	posix = t->text_start-1;
	endix = t->text_length-2;

	t->text_attachments = 0;

	if((cardex->ftype == 1) && (!encrypted)) /* && (t->allow_edit == 0) */
	{
		/* main part of message is HTML */
		if(t->num_attach == 0)
			attachments_extend(t,1);

		t->attach[0].ftype = 0xfaf;
		t->attach[0].displ = posix;
		t->attach[0].length = t->text_length - posix;

		if(t->encoding == ENCODE_QUOTED)
			t->attach[0].encoding = ENCODE_TEXT_NO_HEADER_Q;
		else
			t->attach[0].encoding = ENCODE_TEXT_NO_HEADER;

		t->attach[0].name_displ = 0xff;
		t->attach[0].sprite[0] = 0;
		strcpy(t->attach[0].name,"HTML");

		t->attachments++;
		mime_attachments++;
	}

	if(encrypted)
		posix = endix;   /* don't scan article */

	while(posix < endix)
	{
		if(t->text_base[posix++] != '\n')
		{
			continue;
		}

		if(memcmp(&t->text_base[posix],"-----BEGIN PGP ",15)==0)
			pgp_flag=1;

		p2 = &t->text_base[posix];
		if((*p2 == '-') && (memcmp(p2+1," --",3)==0))
		{
			/* signature delimiter "- -- " a PGP encoded signature */
			p2 += 2;
		}

		if((*p2 == '-') && (p2[1] == '-'))
		{
			/* allow "-- " or "--" as sig separators */

			/*    if((memcmp(p2+2," \n",2)==0) || (p2[2] == '\n'))  */
			if((memcmp(p2+2," \n",2)==0) || (memcmp(p2+2,"=20\n",4)==0))
			{
				/* don't recognise a signature after any attachments */
				if(t->attachments <= 1)  /* 1= the main text of a MIME message */
					t->sig_start = posix;
				continue;
			}

			boundary = -1;
			for(i=0; i<n_boundaries; i++)
			{
				if(memcmp(p2+2,boundary_string[i],boundary_string_len[i])==0)
				{
					boundary = i;
					n_boundaries = i+1;   /* pop stack if we've found a higher level boundary */
					break;
				}
			}

			if(boundary >= 0)
			{
				a = t->attachments;

				if((a > 0) && (a != prev_attachment))
				{
					t->attach[a-1].length = posix - t->attach[a-1].displ;
					prev_attachment = a;
				}

				if(a >= t->num_attach)
					attachments_extend(t,1);

				if(a < t->num_attach)
				{
					/* MIME attachment */
					start = posix;
					t->attach[a].displ = start - 1;
					ftype = 0xfff;
					sprintf(t->attach[a].name,"File%2d",a);
					transfer_encoding = 0;


					while(t->text_base[start++] != '\n');

					if((memcmp(p2+2+boundary_string_len[boundary],"--",2) != 0) &&
							((t->text_length - start) > boundary_string_len[boundary]))
					{
						/* not the end-of-parts marker */
						if(text_interpret_attachment(t,a,start,&transfer_encoding,&ftype) == 1)
							mime_attachments++;
					}

					t->attach[a].length = 0;
					t->attach[a].sprite[0] = 0;
					t->attach[a].encoding = transfer_encoding;
					t->attach[a].ftype = ftype;
				}
			}
		}

		p = &t->text_base[posix];
		if(t->attachments)
		{
			if((*p == 'R') &&
					((memcmp_lc(p,"riscos filetype :",17)==0) || (memcmp_lc(p,"risc os filetype ",17)==0)))
			{
				if((ftype = read_hex(&p[18])) >= 0)
				{
					t->attach[t->attachments-1].ftype = ftype;
				}
			}
		}


		/* ???? SHIFTING HEAP DANGER HERE ???? */

		if(*p == '=')
		{
			if((memcmp(p,"=yend ",6)==0) && (yenc_attachment == 1))
			{
				p3 = p;
				while(*p++ >= ' ');
				t->attach[t->attachments-1].length = posix - t->attach[t->attachments=1].displ + (p-p3);
				yenc_attachment = 0;
			}
			else if((memcmp(p,"=ybegin ",8)==0) && (yenc_attachment == 0))
			{
				/* =ybegin line must end with "name=" */

				for(p3 = p+8; *p3 >= ' '; p3++)
				{
					if(memcmp(p3,"name=",5)==0)
					{
						if(t->attachments >= t->num_attach)
							attachments_extend(t,1);

						if(t->attachments < t->num_attach)
						{
							yenc_attachment = 1;

							if(start_attachments == t->text_length)
								start_attachments = posix;

							offset = p3-p+5;

							t->attach[t->attachments].displ = posix - 1;
							t->attach[t->attachments].sprite[0] = 0;
							t->attach[t->attachments].ftype = 0xffd;
							t->attach[t->attachments].encoding = ENCODE_YENC;
							t->attach[t->attachments].name_displ = offset;

							if(p[offset]=='"')
								offset++;    /* skip " around filename */

							for(ix=0; ix<(sizeof(t->attach[0].name)-1); ix++)
							{
								c = p[ix+offset];
								if(c < ' ')
									break;
								t->attach[t->attachments].name[ix] = c;
							}

							p3 = &t->attach[t->attachments].name[ix];
							do {
								*p3-- = 0;
							} while (isspace(*p3) || (*p3=='"')); /* strip trailing spaces */


							ftype = get_ftype_from_extension(t->attach[t->attachments].name);
							if(ftype >= 0)
							{
								t->attach[t->attachments].ftype = ftype;
							}

							t->attachments++;
						}
						break;
					}
				}
			}
		}  /* end of detect YENC attachment */



		if((*p == 'b') && (transfer_encoding != ENCODE_MIMEXUUE))
		{
			if((memcmp(p,"begin ",6)==0) && isdigit(p[6]) && isdigit(p[7]) && isdigit(p[8]) && ((isspace(p[9])) || (isspace(p[10]))))
			{
				/* further checks for UUE attachment */
				not_uue = 0;
				p3 = p+8;

				/* skip to end of line */
				while((*p3 != '\n') && (*p3 >= ' ')) p3++;

				/* check next line doesn't contain lower case, except at end of
				   the line */
				while(isspace(*p3)) p3++;
				while((*p3 != '\n') && (*p3 >= ' '))
				{
					if(islower(*p3++) && (*p3 != '\n') )
					{
						not_uue = 1;
						break;
					}
				}

				if(not_uue == 0)
				{
					if(t->attachments >= t->num_attach)
						attachments_extend(t,1);

					if(t->attachments < t->num_attach)
					{
						if(start_attachments == t->text_length)
							start_attachments = posix;

						t->attach[t->attachments].displ = posix - 1;
						t->attach[t->attachments].sprite[0] = 0;
						t->attach[t->attachments].ftype = 0xffd;
						t->attach[t->attachments].encoding = ENCODE_UUE;
						t->attach[t->attachments].name_displ = 11;

						offset=10;
						if(isspace(p[offset]))  offset=11;

						for(ix=0; ix<(sizeof(t->attach[0].name)-1); ix++)
						{
							c = p[ix+offset];
							if(c == ',')
							{
								/* filetype number follows */
								if((ftype = read_hex(&p[ix+offset+1])) >= 0)
								{
									if(ftype < 0x1000)
									{
										t->attach[t->attachments].ftype = ftype;
									}
								}
								break;
							}

							if(c < ' ')
								break;
							t->attach[t->attachments].name[ix] = c;
						}
						t->attach[t->attachments].name[ix] = 0;

						if(t->attach[t->attachments].ftype == 0xffd)
						{
							ftype = get_ftype_from_extension(t->attach[t->attachments].name);
							if(ftype >= 0)
							{
								t->attach[t->attachments].ftype = ftype;
							}
						}

						t->attachments++;
					}
				}
			}
		}
	}

	if((t->attachments == 0) && (cardex->status_other2 & STATUS_BIT_ATTACH) &&
			((cardex->status_other & STATUS_BIT_HDR_ONLY)==0) &&
			(pgp_flag == 0) && (!encrypted))
	{
		if(t->num_attach == 0)
			attachments_extend(t,1);
		/* a BASE64 attachment */

		/* we should look for a Content-Type: line in the mail header */
		ftype = 0xffd;  /* data */
		t->attach[0].displ = t->text_start;
		t->attach[0].length = t->text_length - t->text_start;
		t->attach[0].encoding = ENCODE_BASE64_SINGLE;
		t->attach[0].name_displ = 0xff;
		t->attach[0].sprite[0] = 0;
		strcpy(t->attach[0].name,"Data");

		text_interpret_attachment(t,0,0,&transfer_encoding,&ftype);
		if(ftype == -2)
			ftype = 0xffd;
		t->attach[0].ftype = ftype;
		t->attachments = 1;

	}
	else if(mime_attachments > 0)
	{
//      t->attachments--;  /* the final boundary doesn't start an attachment */

		if((t->attachments > 0) && (t->attach[0].ftype == 0xfff) && (t->attach[0].encoding != ENCODE_BASE64))
		{
			/* first attachment is plain text, display it in the article viewer */
			i = t->attach[0].displ + 1;

			while(i < t->text_length)
			{
				if((t->text_base[i] == '\n') && (t->text_base[i+1] == '\n'))
				{
					if(pgp_flag == 0)
						t->internet_header = i+2;

					if(t->attach[0].encoding == ENCODE_QUOTED)
					{
						if(t->attachments > 1)
							end2 = t->attach[1].displ;
						else
							end2 = t->text_length;

						reduction = end2 - decode_quoted_printable(t,i+2,end2);

						move_up(t,end2,reduction);
					}
					break;
				}
				i++;
			}

			t->attachments--;
			memcpy(&t->attach[0],&t->attach[1],sizeof(ATTACHMENT)*t->attachments);
		}
	}
	else if(t->encoding == ENCODE_QUOTED)
	{
		t->text_body_end = t->text_length;
		if(t->attachments)
		{
			t->text_body_end = t->attach[0].displ;
		}
		reduction = t->text_body_end - decode_quoted_printable(t,t->internet_header,t->text_body_end);
		move_up(t,t->text_body_end,reduction);
	}

	t->internet_header2 = t->internet_header;
	t->text_body_end = t->text_length;
	if(t->attachments)
	{
		t->text_body_end = t->attach[0].displ;
	}

	/* decode UTF-8 character set */
	if(t->charset == 102)
	{
		utf8_decode(t,t->internet_header,t->text_body_end);
	}

	if(display)
	{
		if((display==2) && (t->allow_edit == 0))
		{
			/* reorder header lines */
			ix = 0;
			offset = 0;
			for(i=0; i<t->internet_header; i++)
			{
				if(i == reorder_pos[ix])
				{
					len = reorder_len[ix];
					if((offset + len) < sizeof(head_buf))
					{
						memcpy(&head_buf[offset],&t->text_base[i],len);
						offset += len;
						i += (len-1);
					}
					ix++;
				}
				if((i-offset) >= 0)
				{
					t->text_base[i-offset] = t->text_base[i];
				}
			}

			if(offset > 0)
				t->internet_header2 = t->internet_header - offset - 1;

			if((t->text_base[t->internet_header2-1] != '\n') &&
					(t->text_base[t->internet_header2] == '\n'))
				t->internet_header2++;  /* header-only message */

			memcpy(&t->text_base[t->internet_header2],head_buf,offset);
		}

		if(t->show_message_headers)
			t->text_start = 0;
		else
			t->text_start = t->internet_header2;

		t->cursor_index = t->text_start;
		t->cursor_line_start = t->text_start;
		set_caret_pos(t);

		show_attachment_icons(t);
		calc_line_tab(t);    /* up to start of the attachments */

		redraw_text_lines(t,0,-1);

		if(display == 1)
		{
			open_article(t,0x82);   /* re-open with same size and position */
		}
		else if((t->text_type == X_VIEW) && options.move_aviewer && (wstate.flags & wimp_WOPEN))
		{
			open_article(t,0x0b);
		}
		else
		{
			open_article(t,0x59);
		}
	}

	if(n_boundaries > 0)
	{
		t->boundary_string_len = boundary_string_len[0];
		strcpy(t->boundary_string,boundary_string[0]);
	}
	a = t->attachments-1;
	if(a >= 0)
	{
		if(t->attach[a].length == 0)
		{
			t->attach[a].length = t->text_length - t->attach[a].displ;
		}
	}
}   /* end of text_interpret_article */







int text_read_article(TEXTR *t, CARD_EXPANDED *cardex, int addr, int offset, int length,
					  int display, int allow_edit)
/**************************************************************************************/
{
	int  update;


	if(check_lock_lists(0x10014) != 0)   /* allow reason 2 or 4 */
		return(-1);   /* but allow if were are just searching or debatching */

	speak_stop(t);

	if((box_table[cardex->docbox].flags & BOX_LEAVE_UNREAD) || (cardex->status > STATUS_UNREAD))
		update = 0;
	else
		update = 1;

	if(article_read(t,addr,cardex->docbox,offset,length,0,update,0) < 0)
		return(-1);

	t->changed = 0;
	text_window_title(t);

	t->fname_save[0] = 0;

	if(allow_edit)
		t->allow_edit = allow_edit;
	else
		t->allow_edit = box_table[cardex->docbox].flags & BOX_EDITABLE;

	if(t->allow_edit)
		text_autosave(0,t);

	t->just_removed = 0;
	t->cptr = cardex->card_ptr;
	t->filetype = article_filetypes[cardex->ftype];

	text_interpret_article(t,cardex,display);
	return(0);
}   /* end of text_read_article */







void text_set_colours()
/*********************/
/* Set font colour strings from options */
{

	int  i, j;
	int  x;
	int  opt;
	int *colours;
	char *p;

	static char colr_string[8] = {19,0xff,0xff,0xff,0,0,0,8};

	/* white, light-grey, grey, black, green */
	static char bg_colr[5][3] = {0xff,0xff,0xff,  0xee,0xee,0xee, 0xdd,0xdd,0xdd,
								 0x44,0x44,0x44,  0x05,0x87,0x9e
								};

	static char wp_colr[5] = {255,255,255,255,255};   /* 255=transparent */
	static unsigned int  bg_colour[5] = {0xffffff00,0xeeeeee00,0xdddddd00,0x00000000,0x9e870500};  /* window */

	opt = options.colours[COLR_TEXTW];
	if((n_screen_colrs <= 3) && (opt == 1))
		opt = 2;

	if(n_screen_colrs <= 16)
	{
		if(opt==3)
			colours = text_colrs_16_inv;
		else
			colours = text_colrs_16;
	}
	else if(options.define_text_colrs)
		colours = options.text_colrs;
	else
	{
		if(opt==3)
			colours = text_colrs_256_inv;
		else if(opt==4)
			colours = text_colrs_256_green;
		else
			colours = text_colrs_256;
	}

	text_backg_colr = wp_colr[opt];

	text_window_template->colours[wimp_WCWKAREABACK] = text_backg_colr;

	for(i=0; i<N_TEXT_COLRS; i++)
	{
		x = colours[i];
		p = &colr_text[i][4];

		memcpy(colr_text[i],colr_string,8);
		p[0] = x >> 16;
		p[1] = x >> 8;
		p[2] = x;

		for(j=1; j<=3; j++)
		{
			if(opt==3)
			{
				/* don't go all the way to black */
				x = colr_text[i][j+3] * 52;
				colr_text[i][j] = x / 255;
			}
			else
				colr_text[i][j] = bg_colr[opt][j-1];
		}
	}

	colour_backg_fill = wp_colr[opt];
	colour_backg = bg_colour[opt];

	if(opt==3)
	{
		colr_inverse = black_on_white;
		stripe_foreg = 0x0;
		stripe_backg = 0xffffff00;
	}
	else
	{
		colr_inverse = white_on_black;
		stripe_foreg = 0xffffff00;
		stripe_backg = 0;
	}

}   /* end of text_set_colours */




void text_display_change(OPTIONS_DISPLAY *d,int redraw)
/*****************************************************/
/* Redraw 0   don't redraw
          1   redraw after mode change
          2   font change
*/
{
	int  width1, width2;
	int  font;
	int  i;
	TEXTR *t;
	os_error *error;
	os_regset regs;
	wimp_wstate wstate;
	char buf[256];

	page_width = d->text_width * 4;

	font = FONT_TEXT + (options.text_font_toggle & 1);
	text_font = fontn[font];
	text_line_height = font_spacing[font];
	text_font_offset = font_offset[font];

	print_font = fontn[FONT_PRINT];
	print_line_height = font_spacing[FONT_PRINT];


	if(text_font >= 0)
	{
		error = os_swi1(0x4008a+os_X,text_font);   /* Font_SetFont */

		regs.r[0] = text_font;
		regs.r[1] = (int)".M.";
		error = os_swix(0x40097,&regs);   /* Font_StringBBox */
		if(error)
		{
			/* font is not valid, revert to system font.  Don't know why this happen */
			sprintf(buf,"Can't access font handle %d.  %s",text_font,error->errmess);
			werr(0,buf);
			text_font = -1;
		}
		else
		{
			width1 = regs.r[3] - regs.r[1];
			text_max_char_width = width1/scale_x;

			regs.r[1] = (int)"...";
			os_swi(0x40097,&regs);

			width2 = regs.r[3] - regs.r[1];
			if(width1 == width2)
			{
				regs.r[1] = (int)"..";
				os_swi(0x40097,&regs);
				width1 = regs.r[3] - regs.r[1];
				text_monospaced_width = width2 - width1;   /* character width in millipoints */

				width2 = (page_width-margin_l-margin_r) * scale_x;
				text_monospaced = width2 / text_monospaced_width;   /* n. chars per line */
				width2 = (page_width-margin_l-margin_r_reply) * scale_x;
				text_monospaced_reply = width2 / text_monospaced_width;
			}
			else
				text_monospaced = 0;
		}
	}

	if(text_font < 0)
	{
		text_max_char_width = 16;

		text_monospaced_width = 16 * scale_x;
		text_monospaced = (page_width-margin_l-margin_r)/16;   /* system font */
		text_monospaced_reply = (page_width-margin_l-margin_r_reply)/16;

		if(text_monospaced_reply > options.reply_wordwrap)
			text_monospaced_reply = options.reply_wordwrap;
	}

	if(redraw)
	{
		text_set_colours();

		for(i=0; i<N_TEXT_DATA; i++)
		{
			if((t = text_data_record[i]) == NULL)
				continue;

			calc_line_tab(t);
			redraw_text_lines(t,0,-1);

			if(redraw==2)
			{
				wimp_get_wind_state(t->w_card,&wstate);
				if(wstate.flags & wimp_WOPEN)
				{
					open_article(t,0x182);

					/*     if(wstate.flags & wimp_WFOCUS) */
					set_caret_pos(t);
				}
			}
		}
	}
}   /* end of text_display_change */



void dbox_insert_file(dbox d, int field, char *fname)
/***************************************************/
{
	FILE *f;
	int  i;
	char buf[256];

	f = fopen(fname,"r");
	if(f==NULL)
		return;
	i = fread(buf,1,sizeof(buf)-1,f);
	fclose(f);
	buf[i]=0;
	dbox_setfield(d,field,buf);
}   /* end of dbox_insert_file */






BOOL dbox_card_raw_handler(dbox d, void *event, void *handle)
/***********************************************************/
{
	int  ix;
	int  box;
	wimp_mousestr *mouse;
	int bbits;
	int  icon_state;
	int  c;
	int  field;
	TEXTR *t;
	wimp_eventstr *e = event;
	wimp_caretstr caretstr;
	int  filetype;
	char *filename;
	int  max_field;

	t = (TEXTR *)handle;

	switch(e->e)
	{
	case wimp_EOPEN:
		open_card_window(&e->data.o,t);
		return(TRUE);

	case wimp_ECLOSE:
		break;

	case wimp_EPTRLEAVE:
		visdelay2_leave();
		break;

	case wimp_EPTRENTER:
		visdelay2_enter();
		break;

	case wimp_EBUT:
		/* click on icon.  get icon number. */
		mouse = &e->data.but.m;
		bbits = mouse->bbits;
		icon_state = get_button_state(t->w_card,mouse->i);

		switch(dbox_menu_field = mouse->i)
		{
		case CD_DELETE:
			if(mouse->bbits == wimp_BMID)
			{
				card_make_binned_menu(t->fr);
				dbox_menu(wmenu_binned,card_binned_selected,mouse);
				return(TRUE);
			}
			break;

		case CD_LIPS:
			if(bbits & 4)
			{
				/* select button */
				if(akbd_pollsh())
					set_scroll_as_you_speak(t,1);  /* resume scrolling */
				else
					toggle_speak(t,icon_state);
			}
			else if(bbits & 1)
			{
				speak_stop(t);   /* to de-select the icon button */
				options_speak();
			}
			return(TRUE);

		case CD_CATS:
			if(bbits & wimp_BRIGHT)
			{
				/* adjust-click category icon, identify which category and
				   move to front of list */
				wimp_get_caret_pos(&caretstr);
				if(caretstr.i == mouse->i)
				{
					ix = caretstr.index;

					/* get category name from expanded catagory list in expanded card */
					card_identify_cat(t,ix,1);
					open_article(t,0);
				}
				return(TRUE);
			}
			else if(bbits & 0xf)
			{
				current_t = t;
				dbox_menu(category_make_menu(&cty_fr,0),text_selected_source,mouse);
				return(TRUE);
			}
			break;

		case CD_SOURCE:
			current_t = t;
			dbox_menu(category_make_menu(&src_fr,0),text_selected_source,mouse);
			return(TRUE);
			break;

		case CD_ARCHIVE:   /* change box to archive box */
		case CD_BOXIN:
			text_selected_box_copy = 0;
			box = box_table[t->cardex->docbox].archive_to;
			current_t = t;
			if((bbits == wimp_BRIGHT) && (box != 255) && (box != BOX_BIN) && (box != t->cardex->docbox))
			{
				text_selected_box1(box,box_table[t->cardex->docbox].flags & BOX_ARCHIVE_COPY);
			}
			else
			{
				if(akbd_pollsh())
				{
					text_selected_box_copy = 1;
					make_boxes_menu((t->cptr->date_box >> 26) + 0x2800,NULL);
				}
				else
				{
					make_boxes_menu((t->cptr->date_box >> 26) + 0x1900,NULL);
				}

				dbox_menu(wmenu_boxes_names,text_selected_box,mouse);
			}
			return(TRUE);
		}
		break;

	case wimp_EKEY:
		c = e->data.key.chcode;
		field = e->data.key.c.i;

		if(key_text_window1(c,t))
			return(TRUE);

		if(c == '\r')
		{
			text_save_article(t);
		}

		if(((c >= ' ') && (c <= 0xff)) || (c == 8))
		{
			/* printable character or delete */
			mark_changed(t);
		}

		if(t->button_bar_expanded)
			max_field = CD_KEYS;
		else
			max_field = CD_TITLE;

		if((c == KEY_UP) || (c == KEY_SHIFT_TAB))
		{
			if(field == CD_AUTHOR)
			{
				icon_set_caret_pos(t->w_card,max_field,1);
				return(TRUE);
			}
		}
		if((c == '\r') || (c == KEY_DOWN) || (c == KEY_TAB))
		{
			if(field == max_field)
			{
				t->cursor_index = 0;
				t->cursor_line_start = 0;
				t->cursor_line = 0;
				set_caret_pos(t);
				return(TRUE);
			}
		}
		break;

	case wimp_ESEND:
	case wimp_ESENDWANTACK:
		switch(e->data.msg.hdr.action)
		{
		case wimp_MDATASAVE:
			xferrecv_wimpscrap(e);
			return(TRUE);

		case wimp_MDATALOAD:
			filetype = xferrecv_checkinsert(&filename);

			if((e->data.msg.data.dataload.i >> 16) == 1)
				return(FALSE);    /* clipboard response on dbox icon */

			if(filetype == 0xb28)
			{
				if(e->data.msg.data.dataload.i == CD_AUTHOR)
				{
					dbox_insert_file(d,e->data.msg.data.dataload.i,filename);
					return(TRUE);
				}
			}

			if(t->allow_edit)
			{
				attach_create(t,filename,wimpscrap_check(filename),filetype);
			}
			else
				beep();

			wimpscrap_check_clear();
			xferrecv_insertfileok();
			return(TRUE);
		}
		return(help_handler(event,HELP_VIEW));
		break;
	}
	return(FALSE);
}   /* end of dbox_card_raw_handler */





void dbox_card_handler(dbox d, void *handle)
/*******************************************/
{
	int field;
	CARD_EXPANDED *cardex;
	FOLDREC *fr;
	TEXTR *t;
	int  i;
	int  shiftkey;
	int  adjust;
	int  boxlist_recalc_flag=0;

	t = (TEXTR *)handle;
	fr = t->fr;

	cardex = t->cardex;
	field = dbox_get(d);
	shiftkey = akbd_pollsh() << 1;    /* next thread */

	switch(field)
	{
	case CD_SOURCE:
		break;

	case 0:
		t->cursor_index = 0;
		t->cursor_line_start = 0;
		t->cursor_line = 0;
		set_caret_pos(t);
		break;

	case CD_SAVE:
		if(dbox_persist())
		{
			text_save_article(t);
		}
		else
		{
			save_text(t,0);
			cardex->header_changed=0;
		}
		break;

	case CD_PREV:   /* back one */
		speak_stop(t);
		if(dbox_persist())  shiftkey |= 2;

		card_open_next(fr,0+shiftkey,1,t->just_removed);
		break;

	case CD_NEXT:   /* forward one */
		speak_stop(t);
		if(dbox_persist())  shiftkey |= 2;

		card_open_next(fr,1+shiftkey,1,t->just_removed);
		break;

	case CD_THREAD:   /* mark Read and go to next thread */
		speak_stop(t);
		skip_thread(fr,shiftkey ? 2: -1);  /* SHIFT deletes the thread */

		if(dbox_persist())
			i = 2;
		else
			i = 0;
		card_open_next(fr,9+i,1,t->just_removed);
		break;

	case CD_REFERENCES:
		speak_stop(t);
		msgid_list_parents(t);
		break;

	case CD_DELETE:   /* delete */
		speak_stop(t);

		adjust = dbox_persist();

		if(confirm_delete_article(t,cardex))
		{
			card_add_binned_list(cardex->card_ptr);
			if(cardfile_remove(t,cardex->card_ptr,BOX_BIN) < 0)
				break;

			cardex->card_ptr = NULL;
			cardex->header_changed = 0;
		}

		if(adjust) shiftkey |= 2;    /* shift OR adjust to open next UNREAD article */
		if(t->text_type == X_VIEW)
		{
			if(card_open_next(fr,5+shiftkey,1,t->just_removed) != 0)
				close_article_window(t);
		}
		else
		{
			close_article_window(t);
		}
		t->changed = 0;
		break;

	case CD_MKREAD:
		card_remove_fetching_status(cardex);
		if(dbox_persist())
			cardex->status = STATUS_UNREAD;
		else
			cardex->status = STATUS_READ;

		card_changed_status(t->cptr);
		boxlist_recalc_flag = update_unread_count(cardex);
		cardfile_update(fr,t,cardex,1);
		break;

	case CD_MKLOCK:
		card_remove_fetching_status(cardex);

		if(dbox_persist())
			cardex->status = STATUS_MARKED;
		else
			cardex->status = STATUS_LOCKED;

		boxlist_recalc_flag = update_unread_count(cardex);
		cardfile_update(fr,t,cardex,1);
		break;

	case CD_EDIT:
		edit_reply(t,0);
		break;

	case CD_HEADER:
		/* show message headers */
		text_toggle_internet_headers(t);
		break;

	case CD_PRINT:
		print_text_dialog(t,cardex,0);
		break;

	case CD_CATS1:   /* delete category at top of list */
		if(cardex->n_cats > 0)
		{
			for(i=0; i<cardex->n_cats; i++)
			{
				cardex->cat_codes[i] = cardex->cat_codes[i+1];
			}
			cardex->n_cats--;
		}
		/* remake ascii category list */
		strcpy(cardex->cats,expand_cats(cardex));
		dbox_setfield(d,CD_CATS,cardex->cats);
		cardfile_update(fr,t,cardex,0);
		break;

	case CD_LARGER:   /* toggle card button bar height */
		if(t->button_bar_expanded == 2)
		{
			set_card_height(t,cardex,0);
			open_article(t,0x0b);
		}
		else
		{
			set_card_height(t,cardex,2);
			open_article(t,0x2b);
		}
		break;

	case CD_SPELL:
		spell_load_attempt();
		spell_show(t);
		break;

	case CD_FORMAT:
		text_format_text(t);
		break;

	case CD_ADDRADD:
		addrlist_add_email(cardex->author,0);
		break;

	case CD_KILLFROM:
		text_killfile_add(t,KF_AUTHOR);
		break;

	case CD_KILLSUBJ:
		if(cardex->status_other & STATUS_BIT_NEWS)
			text_killfile_add(t,KF_MESSAGE_ID);  /* news thread */
		else
			text_killfile_add(t,KF_TITLE);  /* subject */
		break;

	default:
		text_close_window(t);
		break;
	}

	if(boxlist_recalc_flag==2)
		set_boxlist_extent(7);
	else if(boxlist_recalc_flag==1)
		boxlist_recalc();
}   /* end of dbox_card_handler */




void text_menu_proc(void *handle, char *hit)
/******************************************/
{
	TEXTR *t;
	CARD_EXPANDED *cardex;
	int  hit1;
	int  i;
	int  box_control;
	ARTICLE_OUT *art_out;
	int  boxlist_recalc;

	static char pgp_bits[] = {0x02, 0x01, 0x03, 0x20, 0x10};
	static char pgp_mask[] = {0x11, 0x22, 0x00, 0x11, 0x22};
	static char killfile_type[] = {0,3,4,1,2,5,12,0};

	t = (TEXTR *)handle;

	if(text_menu_flag_url)
	{
		text_menu_url(t,hit);
		return;
	}

	cardex = t->cardex;
	hit1 = hit[1];

	if(t->text_type <= X_VIEW2)
	{
		switch(hit[0])
		{
		case 1:     /* Display */
			switch(hit1)
			{
			case 1:
				format_quoted_text(t);
				break;
			case 2:
				text_swap_font(t);
				break;
			case 3:
				text_remove_tags(t);
				break;
			case 4:
				text_toggle_internet_headers(t);
				break;
			case 5:
				text_rot13(t);
				break;
			case 6:
				pgp_decode(t);
				text_interpret_article(t,t->cardex,1);
				open_article(t,0x83);
				mark_changed2(t);
				break;
			case 7:
				if(pgp_encrypt(t)==0)
				{
					text_interpret_article(t,t->cardex,1);
					mark_changed(t);
				}
				break;
			}
			break;

		case 2:     /* Message */
			box_control = 0x2100;
			text_selected_box_copy = 1;

			switch(hit1)
			{
			case 1:
				text_close_window(t);
				break;
			case 2:
				if(hit[2]==1)
					save_text(t,1);
				break;
			case 3:
				text_save_article(t);
				break;
			case 4:
				print_text_dialog(t,cardex,1);
				break;
			case 5:
				/* drop through to next case */
				text_selected_box_copy = 0;
			case 6:
				current_t = t;
				text_selected_box1(box_lookup_number(hit[2]-1),text_selected_box_copy);
				break;
			case 7:   /* move to archive box */
				current_t = t;
				text_selected_box1(box_table[t->cardex->docbox].archive_to,
								   box_table[t->cardex->docbox].flags & BOX_ARCHIVE_COPY);
				break;
			case 8:
				if(fetch_full(t->fr,t->cptr,0) != 0)
					beep();
				redraw_list_lines(NULL,0);
				break;
			case 9:
				card_add_binned_list(t->cptr);
				text_delete_article(t,0);
				break;
			case 10:
				text_remove_attachments(t);
				break;
			}
			break;

		case 3:      /* Status */
			card_remove_fetching_status(cardex);
			switch(hit1)
			{
			case 1:   /* unread */
				cardex->status = STATUS_UNREAD;
				break;
			case 2:   /* unlock/read */
				cardex->status = STATUS_READ;
				break;
			case 3:   /* lock */
				cardex->status = STATUS_LOCKED;
				break;
			case 4:   /* mark */
				cardex->status = STATUS_MARKED;
				break;
			case 5:   /* add replied */
				cardex->replied = 1;
				break;
			case 6:   /* remove replied */
				cardex->replied = 0;
				break;
			case 7:
				cardex->score = 0;
				sscanf(menu_score_value,"%d",&cardex->score);
				break;
			}

			card_changed_status(t->cptr);
			boxlist_recalc = update_unread_count(cardex);
			cardfile_update(t->fr,t,cardex,1);
			if(boxlist_recalc)
				set_boxlist_extent(7);
			break;

		case 4:      /* Edit */
			switch(hit1)
			{
			case 1:   /* Allow edit */
				text_allow_edit(t);
				break;

			case 2:   /* Quote Text */
				quote_text(t);
				break;

			case 3:   /* Unquote Text */
				unquote_text(t);
				break;
			}
			break;

		case 5:      /* Goto */
			if((t->text_type != X_VIEW) && (hit1 != 1))
			{
				beep();
				break;
			}

			speak_stop(t);
			switch(hit1)
			{
			case 1:
				msgid_list_parents(t);
				break;
			case 2:
				card_open_next(t->fr,0,1,t->just_removed);
				break;
			case 3:
				card_open_next(t->fr,1,1,t->just_removed);
				break;
			case 4:
				if(options.skip_thread==1)
					skip_thread(t->fr,1);
				else
					skip_thread(t->fr,0);
				card_open_next(t->fr,9,1,t->just_removed);
			case 5:
				skip_thread(t->fr,2);
				card_open_next(t->fr,9,1,t->just_removed);
				break;
			}
			break;

		case 6:      /* Send */
			text_menu_send(t,hit1);
			break;

		case 7:      /* Cancel */
			switch(hit1)
			{
			case 1:
				edit_reply(t,0x122);   /* cancel */
				break;
			case 2:
				edit_reply(t,0x232);   /* supersede */
				break;
			}
			break;

		case 8:      /* Misc */
			switch(hit1)
			{
			case 1:
				text_find_string(t);
				break;
			case 2:
				addrlist_add_email(cardex->author,0);
				break;
			case 3:
				toggle_speak(t,1);
				break;
			case 4:
				spell_invoke(t);
				break;
			case 5:
				text_allow_edit(t);
				break;
			case 6:
				text_killfile_add(t,killfile_type[hit[2]]);
				break;
			}
			break;
		}
	}
	else
	{
		/* write mail/news windows */
		switch(hit[0])
		{
		case 1:
			if(hit1==1)
				save_text(t,1);
			break;
		case 2:
			text_find_string(t);
			break;
		case 3:
			addrlist_open(1);
			break;
		case 4:
			toggle_speak(t,1);
			break;
		case 5:
			spell_invoke(t);
			break;
		case 6:
			reply_post_message(t);
			break;
		case 7:   /* edit */
			switch(hit1)
			{
			case 1:
				format_quoted_text(t);
				break;
			case 2:
				text_swap_font(t);
				break;
			case 3:
				text_rot13(t);
				break;
			case 4:
				quote_text(t);
				break;
			case 5:
				unquote_text(t);
				break;
			case 6:
				text_delete_line(t);
				break;
			case 7:    /* set wordwrap */
				if((i = atoi(menu_get_number)) > 0)
				{
					if((i < 30) && (i != 0))
						i = 30;
					t->wordwrap = i;
				}
				break;
			case 8:
				external_edit_toggle(t);
				break;
			}
			mark_changed(t);
			break;
		case 8:   /* PGP */
			art_out = t->art_out;
			switch(hit1)
			{
			case 1:
			case 2:
			case 3:
			case 4:
			case 5:
				i = pgp_bits[hit1-1];
				if(art_out->pgp & i)
				{
					/* already set.  clear this bit */
					art_out->pgp &= (~i);
				}
				else
				{
					art_out->pgp = (art_out->pgp & pgp_mask[hit1-1]) | i;
				}
				break;

			case 6:
				pgp_view_report();
				break;

			case 7:
				pgp_list_keys(1);
				break;
			}
			break;
		}
	}
}   /* end of text_menu_proc */




menu text_menu_maker(void *handle)
/********************************/
{
	TEXTR *t;
	int  fade;
	CARD *cptr;
	int  ix;
	int  i;
	char *sender;
	static menu text_boxes_menu1;
	static menu text_boxes_menu2;
	wimp_mousestr mouse;
	char pgp_set[6];
	char word[400];

	t = (TEXTR *)handle;
	cptr = t->cptr;
	text_menu_flag_url = 0;


	/* are we over a URL in the text ? */
	wimp_get_point_info(&mouse);
	ix = text_click_to_index(t,&mouse,NULL,NULL);
	if(text_is_url(t,ix,word,NULL))
	{
		text_menu_flag_url = 1;
		text_menu_index = ix;
		return(menu_url);
	}

	if(t->text_type > X_VIEW2)
	{
		memset(pgp_set,0,sizeof(pgp_set));
		i = t->art_out->pgp;

		ix = 0;
		if((i & 3)==3)
			pgp_set[3] = 1;
		else if(i & 2)
			pgp_set[1] = 1;
		else if(i & 1)
			pgp_set[2] = 1;

		pgp_set[4] = i & 0x20;
		pgp_set[5] = i & 0x10;

		for(i=1; i<=5; i++)
			menu_setflags(menu_pgp,i,pgp_set[i],0);


		sprintf(menu_get_number,"%d",t->wordwrap);
		return(menu_write);
	}

	fade=1;

	if(cptr->status & STATUS_BIT_NEWS)
	{
		if((cptr->status & STATUS_BIT_OG) && (isalnum(cptr->data[cptr->d_comment])))
		{
			/* log copy of a news message, allow cancel and supersede */
			fade = 0;
		}

		/* allow cancel & supersede if we are the sender */
		sender = reply_extract_email_addr(&cptr->data[cptr->d_author],2);

		for(i=0; i<N_MAIL_BOX; i++)
		{
			if(strcmp(sender,options.mailbox[i].email_addr)==0)
			{
				fade = 0;
				break;
			}
		}
	}


	menu_setflags(menu_viewer,7,0,fade);
	sprintf(menu_score_value,"%d",cptr->score);

	if(t->allow_edit == 0)
		fade = 1;
	else
		fade = 0;
	for(i=2; i<=3; i++)
		menu_setflags(menu_edit2,i,0,fade);

	if(!dbox_persist())
	{
		/* don't do this for adjust-click, only for the initial menu display */
		menu_submenu(menu_viewer,2,0);
		menu_submenu(menu_message,5,0);
		menu_submenu(menu_message,6,0);

		make_boxes_menu((cptr->date_box >> 26) + 0x0900,&text_boxes_menu1);
		menu_submenu(menu_message,5,text_boxes_menu1);
		make_boxes_menu((cptr->date_box >> 26) + 0x0800,&text_boxes_menu2);
		menu_submenu(menu_message,6,text_boxes_menu2);

		menu_submenu(menu_viewer,2,menu_message);
	}
	return(menu_viewer);
}   /* end of text_menu_maker */





void init_dbox_card(TEXTR *t)
/************************/
{
	if(options.article_viewer_type==0)
		t->dbox_card = dbox_new("Viewer");
	else
		t->dbox_card = dbox_new("Viewer2");

	t->w_card = dbox_syshandle(t->dbox_card);

	dbox_raw_eventhandler(t->dbox_card,dbox_card_raw_handler,(void *)t);
	dbox_eventhandler(t->dbox_card, dbox_card_handler, (void *)t);
}   /* end of init_dbox_card */




extern wimp_menustr *wmenu_attach;
extern char menu_get_name[64];


void init_textextra()
/*******************/
{
	static char *menustr_attach = ">Save,Rename,Remove,>Info";
	menu m;
	menu m_write;

	m = menu_new("Attachment",menustr_attach);

	m_write = menu_new("Name:","01234567890");
	menu_make_writeable(m_write,1,menu_get_name,sizeof(menu_get_name),"");
	menu_submenu(m,2,m_write);

	wmenu_attach = (wimp_menustr *)menu_syshandle(m);
}   /* end of init_textextra */





void init_text()
/**************/
{
	menu m;
	menu m2;
	menu m_score;

	/*   text_set_colours(); */


	menu_viewer = m = menu_new("Article View",menustr_view);
	menu_message = menu_new("Message",menustr_message);
	menu_url = menu_new("URL",menustr_url);
	menu_edit2 = menu_new("Edit",menustr_edit2);

	menu_submenu(m,1,menu_new("Display",menustr_display));
	menu_submenu(m,2,menu_message);


	m_score = menu_new("Score","12345");
	menu_make_writeable(m_score,1,menu_score_value,sizeof(menu_score_value),"");
	m2 = menu_new("Status",menustr_status);
	menu_submenu(m2,7,m_score);
	menu_submenu(m,3,m2);


	menu_submenu(m,4,menu_edit2);
	menu_submenu(m,5,menu_new("Goto",menustr_goto));
	menu_submenu(m,6,menu_new("Send",menustr_send));
	menu_submenu(m,7,menu_new("Cancel",menustr_cancel));

	m2 = menu_new("Misc",menustr_misc);
	menu_submenu(m2,6,menu_new("Killfile",menustr_killfile));
	menu_submenu(m,8,m2);

	text_view = text_data_init(TEXT_EXTRA,X_VIEW);
	if(text_view == NULL)
		exit(7);

	init_textextra();

	atexit(terminate_print);
}   /* end of init_text */
